diff --git a/composer.json b/composer.json index 7dc14382..30c8f45d 100644 --- a/composer.json +++ b/composer.json @@ -43,6 +43,7 @@ "knplabs/knp-menu-bundle": "^3.2", "laminas/laminas-feed": "^2.20", "nelmio/cors-bundle": "^2.2", + "nelmio/security-bundle": "^3.0", "okvpn/cron-bundle": "^0.2", "oro/doctrine-extensions": "^2.0", "pagerfanta/core": "^3.7", @@ -92,7 +93,6 @@ "symfony/flex": true, "symfony/runtime": true }, - "optimize-autoloader": true, "preferred-install": { "*": "dist" }, diff --git a/composer.lock b/composer.lock index 55e51988..f2921aa5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f377185c47f6bdcc797d724452c7afb7", + "content-hash": "2ad58b1d7ef7ab90b559874636809dc3", "packages": [ { "name": "babdev/pagerfanta-bundle", @@ -3041,6 +3041,79 @@ }, "time": "2021-12-01T09:34:27+00:00" }, + { + "name": "nelmio/security-bundle", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/nelmio/NelmioSecurityBundle.git", + "reference": "34699d40d81b58b6bd256e34489c799620dff2a4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nelmio/NelmioSecurityBundle/zipball/34699d40d81b58b6bd256e34489c799620dff2a4", + "reference": "34699d40d81b58b6bd256e34489c799620dff2a4", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "symfony/framework-bundle": "^4.4 || ^5.4 || ^6.0", + "symfony/http-kernel": "^4.4 || ^5.4 || ^6.0", + "symfony/security-core": "^4.4 || ^5.4 || ^6.0", + "symfony/security-csrf": "^4.4 || ^5.4 || ^6.0", + "symfony/security-http": "^4.4 || ^5.4 || ^6.0", + "symfony/yaml": "^4.4 || ^5.4 || ^6.0", + "ua-parser/uap-php": "^3.4.4" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpstan/phpstan-symfony": "^1.1", + "phpunit/phpunit": "^9.5", + "psr/cache": "^1.0 || ^2.0 || ^3.0", + "symfony/browser-kit": "^4.4 || ^5.4 || ^6.0", + "symfony/cache": "^4.4 || ^5.4 || ^6.0", + "symfony/phpunit-bridge": "^6.0", + "symfony/twig-bundle": "^4.4 || ^5.4 || ^6.0", + "twig/twig": "^2.10 || ^3.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Nelmio\\SecurityBundle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nelmio", + "homepage": "http://nelm.io" + }, + { + "name": "Symfony Community", + "homepage": "https://github.com/nelmio/NelmioSecurityBundle/contributors" + } + ], + "description": "Extra security-related features for Symfony: signed/encrypted cookies, HTTPS/SSL/HSTS handling, cookie session storage, ...", + "keywords": [ + "security" + ], + "support": { + "issues": "https://github.com/nelmio/NelmioSecurityBundle/issues", + "source": "https://github.com/nelmio/NelmioSecurityBundle/tree/v3.0.0" + }, + "time": "2022-03-17T07:30:15+00:00" + }, { "name": "okvpn/cron-bundle", "version": "0.2.1", @@ -9187,6 +9260,69 @@ ], "time": "2022-12-27T12:28:18+00:00" }, + { + "name": "ua-parser/uap-php", + "version": "v3.9.14", + "source": { + "type": "git", + "url": "https://github.com/ua-parser/uap-php.git", + "reference": "b796c5ea5df588e65aeb4e2c6cce3811dec4fed6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ua-parser/uap-php/zipball/b796c5ea5df588e65aeb4e2c6cce3811dec4fed6", + "reference": "b796c5ea5df588e65aeb4e2c6cce3811dec4fed6", + "shasum": "" + }, + "require": { + "composer/ca-bundle": "^1.1", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.33", + "phpunit/phpunit": "^8 || ^9", + "symfony/console": "^3.4 || ^4.2 || ^4.3 || ^5.0", + "symfony/filesystem": "^3.4 || ^4.2 || ^4.3 || ^5.0", + "symfony/finder": "^3.4 || ^4.2 || ^4.3 || ^5.0", + "symfony/yaml": "^3.4 || ^4.2 || ^4.3 || ^5.0", + "vimeo/psalm": "^3.12" + }, + "suggest": { + "symfony/console": "Required for CLI usage - ^3.4 || ^4.3 || ^5.0", + "symfony/filesystem": "Required for CLI usage - ^3.4 || ^4.3 || ^5.0", + "symfony/finder": "Required for CLI usage - ^3.4 || ^4.3 || ^5.0", + "symfony/yaml": "Required for CLI usage - ^3.4 || ^4.3 || ^5.0" + }, + "bin": [ + "bin/uaparser" + ], + "type": "library", + "autoload": { + "psr-4": { + "UAParser\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dave Olsen", + "email": "dmolsen@gmail.com" + }, + { + "name": "Lars Strojny", + "email": "lars@strojny.net" + } + ], + "description": "A multi-language port of Browserscope's user agent parser.", + "support": { + "issues": "https://github.com/ua-parser/uap-php/issues", + "source": "https://github.com/ua-parser/uap-php/tree/v3.9.14" + }, + "time": "2020-10-02T23:36:20+00:00" + }, { "name": "webmozart/assert", "version": "1.11.0", diff --git a/config/bundles.php b/config/bundles.php index 6ae7e79e..9fc8b2f4 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -15,6 +15,7 @@ Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true], Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true,], BabDev\PagerfantaBundle\BabDevPagerfantaBundle::class => ['all' => true], + Nelmio\SecurityBundle\NelmioSecurityBundle::class => ['all' => true], ]; if (!class_exists(Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class)) { diff --git a/config/packages/nelmio_security.yaml b/config/packages/nelmio_security.yaml new file mode 100644 index 00000000..7135d4ae --- /dev/null +++ b/config/packages/nelmio_security.yaml @@ -0,0 +1,45 @@ +nelmio_security: + clickjacking: + paths: + '^/.*': DENY + + content_type: + nosniff: true + + xss_protection: + enabled: true + mode_block: true + + csp: + enabled: true + report_logger_service: logger + hosts: [] + content_types: [] + enforce: + browser_adaptive: + enabled: false + default-src: + - 'self' + script-src: + - 'self' + - 'unsafe-eval' + connect-src: + - 'self' + img-src: + - 'self' + - 'https:' + - 'data:' + object-src: + - 'none' + style-src: + - 'self' + - 'unsafe-inline' + - 'https://fonts.googleapis.com' + font-src: + - 'self' + - 'https://fonts.googleapis.com' + - 'https://fonts.gstatic.com' + frame-src: + - 'self' + base-uri: + - 'none' diff --git a/public/packeton/css/main.css b/public/packeton/css/main.css index a4da4116..cca48766 100644 --- a/public/packeton/css/main.css +++ b/public/packeton/css/main.css @@ -773,10 +773,12 @@ input:focus:invalid:focus, textarea:focus:invalid:focus, select:focus:invalid:fo font-size: 12px; } -.btn.loading, .btn.loading:hover, .btn.loading:active { +.btn.loading, .btn.loading:hover, .btn.loading:active, .btn.loading:focus { background-image: url("../img/loader.gif"); background-position: 95% 50%; background-repeat: no-repeat; + pointer-events: none; + opacity: 0.75; } .btn-default { diff --git a/public/packeton/js/init.js b/public/packeton/js/init.js new file mode 100644 index 00000000..65871fac --- /dev/null +++ b/public/packeton/js/init.js @@ -0,0 +1,9 @@ +if (!String.prototype.htmlSpecialChars) { + String.prototype.htmlSpecialChars = function () { + return this.replace(/&/g, '&') + .replace(/'/g, ''') + .replace(/"/g, '"') + .replace(//g, '>'); + }; +} diff --git a/public/packeton/js/layout.js b/public/packeton/js/layout.js index 3b1a824f..e66fc5e0 100644 --- a/public/packeton/js/layout.js +++ b/public/packeton/js/layout.js @@ -1,27 +1,52 @@ (function ($, humane) { "use strict"; + $('.view-log').on('click', function (e) { + e.preventDefault(); + let target = $(this); + let details = target.attr('data-details'); + let message = target.attr('data-msg'); + let close = 'x'; + if (message.length > 64) { + if (message.length > 120) { + details = '
' + message + '
' + details; + } + + message = message.substring(0, 60) + '...'; + } + + message = message.htmlSpecialChars(); + humane.log([close, message, details], {timeout: 0}); + + $('a.close').one('click', function () { + humane.remove(); + }); + }); + /** * Ajax error handler */ $.ajaxSetup({ error: function (xhr) { - var resp, message, details = ''; - + let resp, message, details = ''; humane.remove(); + message = ''; if (xhr.responseText) { try { resp = JSON.parse(xhr.responseText); if (resp.status && resp.status === 'error') { message = resp.message; details = resp.details; + } else if (resp.error) { + message = resp.error; } } catch (e) { message = "We're so sorry, something is wrong on our end."; } } + message = message.htmlSpecialChars(); humane.log(details ? [message, details] : message, {timeout: 0, clickToClose: true}); } }); @@ -29,7 +54,7 @@ /** * API Token visibility toggling */ - var token = $('#api-token'); + let token = $('#api-token'); token.val(''); $('.btn-show-api-token,#api-token').each(function() { @@ -38,7 +63,6 @@ token.select(); $('.btn-show-api-token').text('Your API token'); - e.preventDefault(); }); }); diff --git a/public/packeton/js/proxies.js b/public/packeton/js/proxies.js index 35ce1209..53b8fdf1 100644 --- a/public/packeton/js/proxies.js +++ b/public/packeton/js/proxies.js @@ -1,32 +1,6 @@ (function ($, humane) { "use strict"; - if (!String.prototype.htmlSpecialChars) { - String.prototype.htmlSpecialChars = function () { - return this.replace(/&/g, '&') - .replace(/'/g, ''') - .replace(/"/g, '"') - .replace(//g, '>'); - }; - } - - $('.view-log').on('click', function (e) { - e.preventDefault(); - let target = $(this); - let details = target.attr('data-details'); - let message = target.attr('data-msg'); - let close = 'x'; - if (message.length > 64) { - if (message.length > 120) { - details = '
' + message + '
' + details; - } - - message = message.substring(0, 60) + '...'; - } - humane.log([close, message, details], {timeout: 0}); - }); - let gridBtn = $('.grid-buttons .btn'); gridBtn.on('click', (e) => { e.preventDefault(); @@ -37,6 +11,23 @@ dispatchAjaxGridForm(e.target); }); + let updateBtn = $('.update.action').find('.btn'); + updateBtn.on('click', (e) => { + e.preventDefault(); + let data = {}; + let options = ajaxFormData(e.target, data); + if (data['force']) { + if (!window.confirm('All data will remove and resync again. Are you sure?')) { + return; + } + } + + options['success'] = () => { + humane.log('The job has been scheduled'); + } + $.ajax(options); + }); + let packagesMirror = $('#packages_mirror .btn-success'); packagesMirror.on('click', (e) => { e.preventDefault(); @@ -53,6 +44,12 @@ checkbox.prop('checked', true); }); + $('.tabpanel-packages-new').on('click', (e) => { + e.preventDefault(); + let checkbox = $(e.target).closest('.tab-pane').find('.checkbox-new'); + checkbox.prop('checked', true); + }); + function serializeForm(el) { let data = {}; let form = $(el).closest('form'); @@ -96,7 +93,6 @@ return; } - button.removeClass('loading'); let msg = ''; if (data['valid'] && data['valid'].length > 0) { validTextarea = req['packages']; @@ -106,21 +102,33 @@ } if (data['invalid'] && data['invalid'].length > 0) { - console.log(data['invalid']); msg += '
Package not found:' + data['invalid'].join(', ') + '
'; } + if (data['errors'] && data['errors'].length > 0) { + msg += '
' + data['errors'].join('
') + '
'; + } let info = ""; - for (let item of data['validData']) { - let str = item['name'] + " " + item['license'] + " - " + item['description']; - info += str.trim().replace(/[\-,]+$/, '').htmlSpecialChars() + "\n"; + if (data['newData'] && data['newData'].length > 0) { + for (let item of data['newData']) { + let str = item['name'] + " " + item['license'] + " - " + item['description']; + info += str.trim().replace(/[\-,]+$/, '').htmlSpecialChars() + "\n"; + } + msg += 'New packages
' + info + '
'; + } + if (data['updateData'] && data['updateData'].length > 0) { + info = ''; + for (let item of data['updateData']) { + let str = item['name'] + " " + item['license'] + " - " + item['description']; + info += str.trim().replace(/[\-,]+$/, '').htmlSpecialChars() + "\n"; + } + msg += 'Already enabled packages
' + info + '
'; } - msg += 'Valid packages
' + info + '
'; $('#result-validate').html(msg); } - $.ajax(options); + $.ajax(options).always(() => {button.removeClass('loading')}); } function dispatchAjaxGridForm(el) { diff --git a/public/packeton/js/view.js b/public/packeton/js/view.js index a3d4daa4..15b068f0 100644 --- a/public/packeton/js/view.js +++ b/public/packeton/js/view.js @@ -16,24 +16,6 @@ e.preventDefault(); }); - $('.package .view-log').on('click', function (e) { - e.preventDefault(); - - var target = $(this); - var details = target.attr('data-details'); - var message = target.attr('data-msg'); - var close = 'x'; - if (message.length > 64) { - if (message.length > 120) { - details = '
' + message + '
' + details; - } - - message = message.substring(0, 60) + '...'; - } - - humane.log([close, message, details], {timeout: 0}); - }); - $('.package .details-toggler').on('click', function () { var target = $(this); diff --git a/symfony.lock b/symfony.lock index 5e90c926..d5a2b013 100644 --- a/symfony.lock +++ b/symfony.lock @@ -43,6 +43,18 @@ "config/packages/nelmio_cors.yaml" ] }, + "nelmio/security-bundle": { + "version": "3.0", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "2.4", + "ref": "65726efb67ff51d89de38195bc0d230fa811f64d" + }, + "files": [ + "config/packages/nelmio_security.yaml" + ] + }, "okvpn/cron-bundle": { "version": "0.1.1" },