Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split the README into multiple smaller files #209

Merged
merged 1 commit into from
Jul 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
527 changes: 15 additions & 512 deletions README.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
You may want to read [the README in the parent directory](../README.md) first.
14 changes: 14 additions & 0 deletions docs/allow-attributes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Allowing previously disallowed attributes

Disallowed PHP attributes can be allowed again using the same configuration as what methods and functions use. For example, to require `#[Entity]` attribute to always specify `$repositoryClass` argument, you can use configuration similar to the one below. First, we disallow all `#[Entity]` attributes, then re-allow them only if they contain the parameter (with any value):
```neon
parameters:
disallowedAttributes:
-
attribute: Entity
message: 'you must specify $repositoryClass parameter with Entity'
allowParamsAnywhereAnyValue:
-
position: 1
name: repositoryClass
```
18 changes: 18 additions & 0 deletions docs/allow-ignore-errors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
## Allow some previously disallowed calls or attributes

Sometimes, the method, the function, or the constant needs to be called or used once in your code, for example in a custom wrapper. You can use PHPStan's [`ignoreErrors` feature](https://github.com/phpstan/phpstan#ignore-error-messages-with-regular-expressions) to ignore that one call:

```neon
ignoreErrors:
-
message: '#^Calling Redis::connect\(\) is forbidden, use our own Redis instead#' # Needed for the constructor
path: application/libraries/Redis/Redis.php
-
message: '#^Calling print_r\(\) is forbidden, use logger instead#' # Used with $return = true
paths:
- application/libraries/Tls/Certificate.php
- application/libraries/Tls/CertificateSigningRequest.php
- application/libraries/Tls/PublicKey.php
```

The extension's configuration using custom rules is flexible enough to allow a call with specified attributes only for example.
27 changes: 27 additions & 0 deletions docs/allow-in-methods.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## Allow in methods or functions

To allow a previously disallowed method or function only when called from a different method or function in any file, use `allowInFunctions` (or `allowInMethods` alias):

```neon
parameters:
disallowedMethodCalls:
-
method: 'PotentiallyDangerous\Logger::log()'
message: 'use our own logger instead'
allowInMethods:
- Foo\Bar\Baz::method()
```

And vice versa, if you need to disallow a method or a function call only when done from a particular method or function, use `allowExceptInFunctions` (with aliases `allowExceptInMethods`, `disallowInFunctions`, `disallowInMethods`):

```neon
parameters:
disallowedMethodCalls:
-
method: 'Controller::redirect()'
message: 'redirect in startup() instead'
allowExceptInMethods:
- Controller\Foo\Bar\*::__construct()
```

The function or method names support [fnmatch()](https://www.php.net/function.fnmatch) patterns.
46 changes: 46 additions & 0 deletions docs/allow-in-paths.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
## Allow in paths

You can allow some previously disallowed calls and usages using the `allowIn` configuration key, for example:

```neon
parameters:
disallowedMethodCalls:
-
method: 'PotentiallyDangerous\Logger::log()'
message: 'use our own logger instead'
allowIn:
- path/to/some/file-*.php
- tests/*.test.php
```

Paths in `allowIn` support [fnmatch()](https://www.php.net/function.fnmatch) patterns.

Relative paths in `allowIn` are resolved based on the current working directory. When running PHPStan from a directory or subdirectory which is not your "root" directory, the paths will probably not work.
Use `filesRootDir` in that case to specify an absolute root directory for all `allowIn` paths. Absolute paths might change between machines (for example your local development machine and a continuous integration machine) but you
can use [`%rootDir%`](https://phpstan.org/config-reference#expanding-paths) to start with PHPStan's root directory (usually `/something/something/vendor/phpstan/phpstan`) and then `..` from there to your "root" directory.

For example when PHPStan is installed in `/home/foo/vendor/phpstan/phpstan` and you're using a configuration like this:
```neon
parameters:
filesRootDir: %rootDir%/../../..
disallowedMethodCalls:
-
method: 'PotentiallyDangerous\Logger::log()'
allowIn:
- path/to/some/file-*.php
```
then `Logger::log()` will be allowed in `/home/foo/path/to/some/file-bar.php`.

If you need to disallow a methods or a function call, a constant, a namespace, a class, a superglobal, or an attribute usage only in certain paths, as an inverse of `allowIn`, you can use `allowExceptIn` (or the `disallowIn` alias):
```neon
parameters:
filesRootDir: %rootDir%/../../..
disallowedMethodCalls:
-
method: 'PotentiallyDangerous\Logger::log()'
allowExceptIn:
- path/to/some/dir/*.php
```
This will disallow `PotentiallyDangerous\Logger::log()` calls in `%rootDir%/../../../path/to/some/dir/*.php`.

Please note that before version 2.15, `filesRootDir` was called `allowInRootDir` which is still supported, but deprecated.
28 changes: 28 additions & 0 deletions docs/allow-with-flags.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
## Allow with specified parameter flags only

Some functions can be called with _flags_ or _bitmasks_, for example

```php
json_encode($foo, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT);
```
Let's say you want to disallow `json_encode()` except when called with `JSON_HEX_APOS` (integer `4`) flag. In the call above, the value of the second parameter (`JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT`) is `13` (`1 | 4 | 8`).
For the extension to be able to "find" the `4` in `13`, you need to use the `ParamFlags` family of config options:

- `allowParamFlagsInAllowed`
- `allowParamFlagsAnywhere`
- `allowExceptParamFlagsInAllowed` or `disallowParamFlagsInAllowed`
- `allowExceptParamFlags` or `disallowParamFlags`

They work like their non-flags `Param` counterparts except they're looking if specific bits in the mask parameter are set.

The `json_encode()` example mentioned above would look like the following snippet:

```neon
parameters:
disallowedFunctionCalls:
function: 'json_encode'
allowParamFlagsAnywhere:
-
position: 2
value: ::JSON_HEX_APOS
```
170 changes: 170 additions & 0 deletions docs/allow-with-parameters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
## Allow with specified parameters only

You can also narrow down the allowed items when called with some parameters (applies only to disallowed method, static & function calls, for obvious reasons). _Please note that for now, only scalar values are supported in the configuration, not arrays._

For example, you want to disallow calling `print_r()` but want to allow `print_r(..., true)`.
This can be done with optional `allowParamsInAllowed` or `allowParamsAnywhere` configuration keys:

```neon
parameters:
disallowedMethodCalls:
-
method: 'PotentiallyDangerous\Logger::log()'
message: 'use our own logger instead'
allowIn:
- path/to/some/file-*.php
- tests/*.test.php
allowParamsInAllowed:
-
position: 1
name: 'message'
value: 'foo'
-
position: 2
name: 'alert'
value: true
allowParamsAnywhere:
-
position: 2
name: 'alert'
value: true
```

When using `allowParamsInAllowed`, calls will be allowed only when they are in one of the `allowIn` paths, and are called with all parameters listed in `allowParamsInAllowed`.
With `allowParamsAnywhere`, calls are allowed when called with all parameters listed no matter in which file. In the example above, the `log()` method will be disallowed unless called as:
- `log(..., true)` (or `log(..., alert: true)`) anywhere
- `log('foo', true)` (or `log(message: 'foo', alert: true)`) in `another/file.php` or `optional/path/to/log.tests.php`

Use `allowParamsInAllowedAnyValue` and `allowParamsAnywhereAnyValue` if you don't care about the parameter's value but want to make sure the parameter is passed.
Following the previous example:

```neon
parameters:
disallowedMethodCalls:
-
method: 'PotentiallyDangerous\Logger::log()'
message: 'use our own logger instead'
allowIn:
- path/to/some/file-*.php
- tests/*.test.php
allowParamsInAllowedAnyValue:
-
position: 2
name: 'alert'
allowParamsAnywhereAnyValue:
-
position: 1
name: 'message'
```
means that you should use (`...` means any value):
- `log(...)` (or `log(message: ...)`) anywhere
- `log(..., ...)` (or `log(message: ..., alert: ...)`) in `another/file.php` or `optional/path/to/log.tests.php`

Such configuration only makes sense when both the parameters of `log()` are optional. If they are required, omitting them would result in an error already detected by PHPStan itself.

### Allow calls except when a param has a specified value

Sometimes, it's handy to disallow a function or a method call only when a parameter matches a configured value but allow it otherwise. _Please note that currently only scalar values are supported, not arrays._

For example the `hash()` function, it's fine using it with algorithm families like SHA-2 & SHA-3 (not for passwords though) but you'd like PHPStan to report when it's used with MD5 like `hash('md5', ...)`.
You can use `allowExceptParams` (or `disallowParams`), `allowExceptCaseInsensitiveParams` (or `disallowCaseInsensitiveParams`), `allowExceptParamsInAllowed` (or `disallowParamsInAllowed`) config options to disallow only some calls:

```neon
parameters:
disallowedFunctionCalls:
-
function: 'hash()'
allowExceptCaseInsensitiveParams:
-
position: 1
name: 'algo'
value: 'md5'
```

This will disallow `hash()` call where the first parameter (or the named parameter `algo`) is `'md5'`. `allowExceptCaseInsensitiveParams` is used because the first parameter of `hash()` is case-insensitive (so you can also use `'MD5'`, or even `'Md5'` & `'mD5'` if you wish).
To disallow only exact matches, use `allowExceptParams`:

```neon
parameters:
disallowedFunctionCalls:
-
function: 'foo()'
allowExceptParams:
-
position: 2
value: 'baz'
```
will disallow `foo('bar', 'baz')` but not `foo('bar', 'BAZ')`.

It's also possible to disallow functions and methods previously allowed by path (using `allowIn`) or by function/method name (`allowInMethods`) when they're called with specified parameters, and allow when called with any other parameter. This is done using the `allowExceptParamsInAllowed` config option.

Take this example configuration:

```neon
parameters:
disallowedFunctionCalls:
-
function: 'waldo()'
allowIn:
- 'views/*'
allowExceptParamsInAllowed:
-
position: 2
value: 'quux'
```

Calling `waldo()` is disallowed, and allowed back again only when the file is in the `views/` subdirectory **and** `waldo()` is called in the file with a 2nd parameter being the string `quux`.

As already demonstrated above, named parameters are also supported:

```neon
parameters:
disallowedFunctionCalls:
-
function: 'json_decode()'
message: 'set the $flags parameter to `JSON_THROW_ON_ERROR` to throw a JsonException'
allowParamsAnywhere:
-
position: 4
name: 'flags'
value: ::JSON_THROW_ON_ERROR
```

This format allows to detect the value in both cases whether it's used with a traditional positional parameter (e.g. `json_decode($foo, null, 512, JSON_THROW_ON_ERROR)`) or a named parameter (e.g. `json_decode($foo, flags: JSON_THROW_ON_ERROR)`).
All keys are optional but if you don't specify `name`, the named parameter will not be found in a call like e.g. `json_decode($foo, null, 512, JSON_THROW_ON_ERROR)`.
And vice versa, if you don't specify the `position` key, only the named parameter will be found matching this definition, not the positional one.

You can use shortcuts like
```neon
parameters:
disallowedFunctionCalls:
# ...
allowParamsAnywhere:
2: true
foo: 'bar'
allowParamsAnywhereAnyValue:
- 2
- foo
```

which internally expands to

```neon
parameters:
disallowedFunctionCalls:
# ...
allowParamsAnywhere:
-
position: 2
value: true
-
name: foo
value: 'bar'
allowParamsAnywhereAnyValue:
-
position: 2
-
name: foo
```

But because the "positional _or_ named" limitation described above applies here as well, I generally don't recommend using these shortcuts and instead recommend specifying both `position` and `name` keys.
40 changes: 40 additions & 0 deletions docs/configuration-bundled.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
## Using bundled configuration files

You can start by including `disallowed-dangerous-calls.neon` in your `phpstan.neon`:

```neon
includes:
- vendor/spaze/phpstan-disallowed-calls/disallowed-dangerous-calls.neon
```

`disallowed-dangerous-calls.neon` can also serve as a template when you'd like to extend the configuration to disallow some other functions or methods, copy it and modify to your needs.
You can also allow a previously disallowed dangerous call in a defined path (see below) in your own config by using the same `call` or `method` key.

If you want to disallow program execution functions (`exec()`, `shell_exec()` & friends) including the backtick operator (`` `...` ``, disallowed when `shell_exec()` is disallowed), include `disallowed-execution-calls.neon`:

```neon
includes:
- vendor/spaze/phpstan-disallowed-calls/disallowed-execution-calls.neon
```

I'd recommend you include both:

```neon
includes:
- vendor/spaze/phpstan-disallowed-calls/disallowed-dangerous-calls.neon
- vendor/spaze/phpstan-disallowed-calls/disallowed-execution-calls.neon
```

To disallow some insecure or potentially insecure calls (like `md5()`, `sha1()`, `mysql_query()`), include `disallowed-insecure-calls.neon`:

```neon
includes:
- vendor/spaze/phpstan-disallowed-calls/disallowed-insecure-calls.neon
```

Some function calls are better when done for example with some parameters set to a defined value ("strict calls"). For example `in_array()` better also check for types to prevent some type juggling bugs. Include `disallowed-loose-calls.neon` to disallow calls without such parameters set ("loose calls").

```neon
includes:
- vendor/spaze/phpstan-disallowed-calls/disallowed-loose-calls.neon
```
Loading