├── .gitignore ├── .github ├── FUNDING.yml └── workflows │ └── php.yml ├── tests ├── RegistersPackage.php ├── ServiceProviderTest.php └── PendingSanitizationTest.php ├── phpunit.xml ├── src ├── LarasaneServiceProvider.php ├── Facades │ └── Sanitizer.php ├── BuildsSanitizer.php └── PendingSanitization.php ├── composer.json ├── config └── larasane.php └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /.phpunit.cache 2 | .phpunit.result.cache 3 | vendor 4 | composer.lock -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # Help me support this package 2 | 3 | ko_fi: DarkGhostHunter 4 | custom: ['https://paypal.me/darkghosthunter'] -------------------------------------------------------------------------------- /tests/RegistersPackage.php: -------------------------------------------------------------------------------- 1 | 'DarkGhostHunter\Larasane\Facades\Sanitizer' 16 | ]; 17 | } 18 | } -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | src/ 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | tests 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/LarasaneServiceProvider.php: -------------------------------------------------------------------------------- 1 | mergeConfigFrom(__DIR__ . '/../config/larasane.php', 'larasane'); 18 | 19 | $this->app->singleton(SanitizerBuilder::class, static function () { 20 | return SanitizerBuilder::createDefault(); 21 | }); 22 | 23 | $this->app->bind(PendingSanitization::class, static function ($app) { 24 | return new PendingSanitization($app[SanitizerBuilder::class], $app['config']); 25 | }); 26 | } 27 | 28 | /** 29 | * Bootstrap any application services. 30 | * 31 | * @return void 32 | */ 33 | public function boot(): void 34 | { 35 | if ($this->app->runningInConsole()) { 36 | $this->publishes([__DIR__ . '/../config/larasane.php' => $this->app->configPath('larasane.php')], 'config'); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/Facades/Sanitizer.php: -------------------------------------------------------------------------------- 1 | app->getLoadedProviders()); 19 | } 20 | 21 | public function test_registers_facade(): void 22 | { 23 | static::assertInstanceOf(PendingSanitization::class, Sanitizer::getFacadeRoot()); 24 | } 25 | 26 | public function test_publishes_config(): void 27 | { 28 | static::assertEquals(include(__DIR__ . '/../config/larasane.php'), config('larasane')); 29 | } 30 | 31 | public function test_registers_pending_sanitization(): void 32 | { 33 | $this->artisan( 34 | 'vendor:publish', 35 | [ 36 | '--provider' => 'DarkGhostHunter\Larasane\LarasaneServiceProvider', 37 | '--tag' => 'config', 38 | ] 39 | )->execute(); 40 | 41 | static::assertFileEquals(base_path('config/larasane.php'), __DIR__ . '/../config/larasane.php'); 42 | } 43 | 44 | public function test_registers_bindings(): void 45 | { 46 | static::assertArrayHasKey(SanitizerBuilder::class, $this->app->getBindings()); 47 | static::assertArrayHasKey(PendingSanitization::class, $this->app->getBindings()); 48 | } 49 | } -------------------------------------------------------------------------------- /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | test: 9 | 10 | runs-on: ubuntu-latest 11 | strategy: 12 | fail-fast: true 13 | matrix: 14 | php: [8.0, 7.4] 15 | laravel: [8.*, 7.*] 16 | dependency-version: [prefer-lowest, prefer-stable] 17 | include: 18 | - laravel: 8.* 19 | testbench: ^6.17 20 | - laravel: 7.* 21 | testbench: ^5.18 22 | 23 | name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }} - ${{ matrix.dependency-version }} 24 | 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v2 28 | 29 | - name: Setup PHP 30 | uses: shivammathur/setup-php@v2 31 | with: 32 | php-version: ${{ matrix.php }} 33 | extensions: mbstring, intl 34 | coverage: xdebug 35 | 36 | - name: Cache dependencies 37 | uses: actions/cache@v2 38 | with: 39 | path: ~/.composer/cache/files 40 | key: ${{ runner.os }}-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} 41 | restore-keys: ${{ runner.os }}-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer- 42 | 43 | - name: Install dependencies 44 | run: | 45 | composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-progress --no-update 46 | composer update --${{ matrix.dependency-version }} --prefer-dist --no-progress --no-suggest 47 | 48 | - name: Run Tests 49 | run: composer run-script test 50 | 51 | - name: Upload Coverage to Coveralls 52 | env: 53 | COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} 54 | COVERALLS_SERVICE_NAME: github 55 | run: | 56 | rm -rf composer.* vendor/ 57 | composer require php-coveralls/php-coveralls 58 | vendor/bin/php-coveralls -------------------------------------------------------------------------------- /config/larasane.php: -------------------------------------------------------------------------------- 1 | 1000, 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Code allowance 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Larasane only allows for basic inoffensive tags in HTML. While this can 24 | | be enough for most apps, you can allow for more tags and have control 25 | | over each tag attributes. Anyway consider starting from the bottom. 26 | | 27 | | See: https://github.com/tgalopin/html-sanitizer/blob/1.4.0/docs/1-getting-started.md#extensions 28 | | 29 | */ 30 | 31 | 'allow_code' => [ 32 | 'basic', /* 'list', 'table', 'image', 'code', 'iframe', 'details', 'extra' */ 33 | ], 34 | 35 | /* 36 | |-------------------------------------------------------------------------- 37 | | Links security 38 | |-------------------------------------------------------------------------- 39 | | 40 | | Links are allowed in , and '; 114 | 115 | static::assertEquals( 116 | 'Look into ', 117 | Sanitizer::input($input)->sanitize() 118 | ); 119 | } 120 | 121 | public function test_does_not_enforces_http(): void 122 | { 123 | config()->set('larasane.allow_code', ['basic', 'image', 'iframe']); 124 | 125 | $input = 'Go to My Site.'; 126 | 127 | static::assertEquals( 128 | $input, 129 | Sanitizer::input($input)->enforceHttps(false)->sanitize() 130 | ); 131 | 132 | $input = 'Check my image'; 133 | 134 | static::assertEquals( 135 | $input, 136 | Sanitizer::input($input)->enforceHttps(false)->sanitize() 137 | ); 138 | 139 | $input = 'Look into '; 140 | 141 | static::assertEquals( 142 | $input, 143 | Sanitizer::input($input)->enforceHttps(false)->sanitize() 144 | ); 145 | } 146 | 147 | public function test_defaults_doesnt_allows_data_image(): void 148 | { 149 | config()->set('larasane.allow_code', ['image']); 150 | 151 | $input = 'Check my image.'; 152 | 153 | static::assertEquals( 154 | 'Check my image.', 155 | Sanitizer::input($input)->sanitize() 156 | ); 157 | } 158 | 159 | public function test_overrides_data_image(): void 160 | { 161 | config()->set('larasane.allow_code', ['image']); 162 | 163 | $input = 'Check my image.'; 164 | 165 | static::assertEquals( 166 | $input, 167 | Sanitizer::input($input)->imageData(true)->sanitize() 168 | ); 169 | } 170 | 171 | public function test_defaults_no_mailto_link(): void 172 | { 173 | $input = 'Send email to me.'; 174 | 175 | static::assertEquals( 176 | 'Send email to me.', 177 | Sanitizer::input($input)->sanitize() 178 | ); 179 | } 180 | 181 | public function test_overrides_mailto_link(): void 182 | { 183 | $input = 'Send email to me.'; 184 | 185 | static::assertEquals( 186 | $input, 187 | Sanitizer::input($input)->allowMailto()->sanitize() 188 | ); 189 | } 190 | 191 | public function test_overrides_tags_attributes(): void 192 | { 193 | $input = 'This is important.'; 194 | 195 | static::assertEquals( 196 | 'This is important.', 197 | Sanitizer::input($input)->sanitize() 198 | ); 199 | 200 | static::assertEquals( 201 | $input, 202 | Sanitizer::input($input)->tagAttributes('strong', 'class')->sanitize() 203 | ); 204 | } 205 | 206 | public function test_overrides_tag_config(): void 207 | { 208 | $input = 'Send email to me.'; 209 | 210 | static::assertEquals( 211 | $input, 212 | Sanitizer::input($input)->tagConfig('a', 'allow_mailto', true)->sanitize() 213 | ); 214 | } 215 | 216 | protected function tearDown(): void 217 | { 218 | @unlink($this->app->configPath('larasane.php')); 219 | 220 | parent::tearDown(); 221 | } 222 | } --------------------------------------------------------------------------------