@appland/scanner 1.40.3 → 1.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -39
- package/built/appMapIndex.js +40 -0
- package/built/appMapIndex.js.map +1 -0
- package/built/check.js +3 -3
- package/built/check.js.map +1 -1
- package/built/checkInstance.js +4 -4
- package/built/checkInstance.js.map +1 -1
- package/built/cli/ci/command.js +21 -26
- package/built/cli/ci/command.js.map +1 -1
- package/built/cli/fail.js +13 -0
- package/built/cli/fail.js.map +1 -0
- package/built/cli/merge/command.js +101 -0
- package/built/cli/merge/command.js.map +1 -0
- package/built/cli/merge/options.js +3 -0
- package/built/cli/merge/options.js.map +1 -0
- package/built/cli/reportUploadURL.js +11 -0
- package/built/cli/reportUploadURL.js.map +1 -0
- package/built/cli/scan/command.js +5 -1
- package/built/cli/scan/command.js.map +1 -1
- package/built/cli/scan/scanner.js +2 -2
- package/built/cli/scan/scanner.js.map +1 -1
- package/built/cli/scan.js +4 -2
- package/built/cli/scan.js.map +1 -1
- package/built/cli/updateCommitStatus.js +65 -0
- package/built/cli/updateCommitStatus.js.map +1 -0
- package/built/cli/upload/command.js +10 -5
- package/built/cli/upload/command.js.map +1 -1
- package/built/cli.js +2 -0
- package/built/cli.js.map +1 -1
- package/built/database/index.js +136 -161
- package/built/database/index.js.map +1 -1
- package/built/integration/appland/{fetchStatus.js → app/listFindingStatus.js} +1 -1
- package/built/integration/appland/app/listFindingStatus.js.map +1 -0
- package/built/integration/appland/{appMap.js → appMap/create.js} +43 -48
- package/built/integration/appland/appMap/create.js.map +1 -0
- package/built/integration/appland/location.js +3 -0
- package/built/integration/appland/location.js.map +1 -0
- package/built/integration/appland/{mapset.js → mapset/create.js} +41 -46
- package/built/integration/appland/mapset/create.js.map +1 -0
- package/built/integration/appland/{upload.js → scannerJob/create.js} +27 -19
- package/built/integration/appland/scannerJob/create.js.map +1 -0
- package/built/integration/appland/scannerJob/merge.js +92 -0
- package/built/integration/appland/scannerJob/merge.js.map +1 -0
- package/built/integration/appland/scannerJob.js +3 -0
- package/built/integration/appland/scannerJob.js.map +1 -0
- package/built/report/summaryReport.js +1 -1
- package/built/report/summaryReport.js.map +1 -1
- package/built/ruleChecker.js +12 -11
- package/built/ruleChecker.js.map +1 -1
- package/built/rules/authzBeforeAuthn.js +6 -0
- package/built/rules/authzBeforeAuthn.js.map +1 -1
- package/built/rules/circularDependency.js +4 -0
- package/built/rules/circularDependency.js.map +1 -1
- package/built/rules/deserializationOfUntrustedData.js +8 -0
- package/built/rules/deserializationOfUntrustedData.js.map +1 -1
- package/built/rules/http500.js +7 -0
- package/built/rules/http500.js.map +1 -1
- package/built/rules/illegalPackageDependency.js +7 -0
- package/built/rules/illegalPackageDependency.js.map +1 -1
- package/built/rules/incompatibleHttpClientRequest.js +7 -0
- package/built/rules/incompatibleHttpClientRequest.js.map +1 -1
- package/built/rules/insecureCompare.js +4 -0
- package/built/rules/insecureCompare.js.map +1 -1
- package/built/rules/jobNotCancelled.js +3 -0
- package/built/rules/jobNotCancelled.js.map +1 -1
- package/built/rules/lib/matchEvent.js +3 -4
- package/built/rules/lib/matchEvent.js.map +1 -1
- package/built/rules/lib/parseRuleDescription.js +18 -0
- package/built/rules/lib/parseRuleDescription.js.map +1 -0
- package/built/rules/logoutWithoutSessionReset.js +8 -0
- package/built/rules/logoutWithoutSessionReset.js.map +1 -1
- package/built/rules/missingAuthentication.js +6 -0
- package/built/rules/missingAuthentication.js.map +1 -1
- package/built/rules/missingContentType.js +6 -0
- package/built/rules/missingContentType.js.map +1 -1
- package/built/rules/nPlusOneQuery.js +8 -2
- package/built/rules/nPlusOneQuery.js.map +1 -1
- package/built/rules/queryFromInvalidPackage.js +6 -0
- package/built/rules/queryFromInvalidPackage.js.map +1 -1
- package/built/rules/queryFromView.js +6 -0
- package/built/rules/queryFromView.js.map +1 -1
- package/built/rules/rpcWithoutCircuitBreaker.js +6 -0
- package/built/rules/rpcWithoutCircuitBreaker.js.map +1 -1
- package/built/rules/saveWithoutValidation.js +6 -0
- package/built/rules/saveWithoutValidation.js.map +1 -1
- package/built/rules/secretInLog.js +3 -0
- package/built/rules/secretInLog.js.map +1 -1
- package/built/rules/slowFunctionCall.js +6 -0
- package/built/rules/slowFunctionCall.js.map +1 -1
- package/built/rules/slowHttpServerRequest.js +6 -0
- package/built/rules/slowHttpServerRequest.js.map +1 -1
- package/built/rules/slowQuery.js +6 -0
- package/built/rules/slowQuery.js.map +1 -1
- package/built/rules/tooManyJoins.js +9 -3
- package/built/rules/tooManyJoins.js.map +1 -1
- package/built/rules/tooManyUpdates.js +6 -0
- package/built/rules/tooManyUpdates.js.map +1 -1
- package/built/rules/unbatchedMaterializedQuery.js +9 -4
- package/built/rules/unbatchedMaterializedQuery.js.map +1 -1
- package/built/rules/updateInGetRequest.js +6 -0
- package/built/rules/updateInGetRequest.js.map +1 -1
- package/built/scope/sqlTransactionScope.js +3 -2
- package/built/scope/sqlTransactionScope.js.map +1 -1
- package/built/sqlWarning.js +56 -0
- package/built/sqlWarning.js.map +1 -0
- package/doc/architecture.md +48 -0
- package/doc/labels/audit.md +7 -0
- package/doc/labels/dao.materialize.md +12 -0
- package/doc/labels/deserialize.safe.md +9 -0
- package/doc/labels/deserialize.unsafe.md +12 -0
- package/doc/labels/http.session.clear.md +7 -0
- package/doc/labels/job.cancel.md +11 -0
- package/doc/labels/job.create.md +13 -0
- package/doc/labels/log.md +12 -0
- package/doc/labels/public.md +8 -0
- package/doc/labels/rpc.circuit_breaker.md +16 -0
- package/doc/labels/sanitize.md +29 -0
- package/doc/labels/secret.md +11 -0
- package/doc/labels/security.authentication.md +10 -0
- package/doc/labels/security.authorization.md +9 -0
- package/doc/labels/security.logout.md +9 -0
- package/doc/labels/string.equals.md +18 -0
- package/doc/rules/authzBeforeAuthn.md +47 -0
- package/doc/rules/circularDependency.md +57 -0
- package/doc/rules/deserializationOfUntrustedData.md +55 -0
- package/doc/rules/http500.md +36 -0
- package/doc/rules/illegalPackageDependency.md +50 -0
- package/doc/rules/incompatibleHttpClientRequest.md +35 -0
- package/doc/rules/insecureCompare.md +59 -0
- package/doc/rules/jobNotCancelled.md +49 -0
- package/doc/rules/logoutWithoutSessionReset.md +40 -0
- package/doc/rules/missingAuthentication.md +59 -0
- package/doc/rules/missingContentType.md +33 -0
- package/doc/rules/nPlusOneQuery.md +52 -0
- package/doc/rules/queryFromInvalidPackage.md +45 -0
- package/doc/rules/queryFromView.md +42 -0
- package/doc/rules/rpcWithoutCircuitBreaker.md +44 -0
- package/doc/rules/saveWithoutValidation.md +33 -0
- package/doc/rules/secretInLog.md +49 -0
- package/doc/rules/slowFunctionCall.md +39 -0
- package/doc/rules/slowHttpServerRequest.md +34 -0
- package/doc/rules/slowQuery.md +33 -0
- package/doc/rules/tooManyJoins.md +40 -0
- package/doc/rules/tooManyUpdates.md +46 -0
- package/doc/rules/unbatchedMaterializedQuery.md +54 -0
- package/doc/rules/updateInGetRequest.md +44 -0
- package/package.json +10 -6
- package/built/integration/appland/appMap.js.map +0 -1
- package/built/integration/appland/fetchStatus.js.map +0 -1
- package/built/integration/appland/mapset.js.map +0 -1
- package/built/integration/appland/upload.js.map +0 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule: query-from-view
|
|
3
|
+
name: Query from view
|
|
4
|
+
title: Queries from view
|
|
5
|
+
references:
|
|
6
|
+
CWE-1057: https://cwe.mitre.org/data/definitions/1057.html
|
|
7
|
+
impactDomain: Maintainability
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
Ensures that SQL queries are not performed directly from the view layer. This helps to make the code
|
|
11
|
+
more maintainable by encapsulating access to the database.
|
|
12
|
+
|
|
13
|
+
### Notes
|
|
14
|
+
|
|
15
|
+
Performing SQL queries directly from the view layer introduces several maintainability concerns:
|
|
16
|
+
|
|
17
|
+
1. View logic is tied to the server, and cannot be refactored to the client side.
|
|
18
|
+
2. Database interactions are harder to test, because they depend on details of the view
|
|
19
|
+
implementation.
|
|
20
|
+
3. Performance can be adversely and unexpectedly affected by minor changes to the view.
|
|
21
|
+
|
|
22
|
+
### Rule logic
|
|
23
|
+
|
|
24
|
+
Each query is tested to see if it has an ancestor event with the view label.
|
|
25
|
+
|
|
26
|
+
### Resolution
|
|
27
|
+
|
|
28
|
+
Data objects which are passed to the view layer for rendering should not have access to the
|
|
29
|
+
database. Disable database access in some way, or transfer data from DAO objects into plain old
|
|
30
|
+
structs.
|
|
31
|
+
|
|
32
|
+
### Options
|
|
33
|
+
|
|
34
|
+
- `forbiddenLabel`. Label which identifies the view layer. Default: `mvc.template`.
|
|
35
|
+
|
|
36
|
+
### Examples
|
|
37
|
+
|
|
38
|
+
```yaml
|
|
39
|
+
- rule: queryFromView
|
|
40
|
+
properties:
|
|
41
|
+
forbiddenLabel: mvc.template
|
|
42
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule: rpc-without-circuit-breaker
|
|
3
|
+
name: RPC without circuit breaker
|
|
4
|
+
title: RPC without circuit breaker
|
|
5
|
+
impactDomain: Stability
|
|
6
|
+
labels:
|
|
7
|
+
- rpc.circuit_breaker
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
Identifies HTTP client requests which do not utilize a
|
|
11
|
+
[circuit breaker](https://martinfowler.com/bliki/CircuitBreaker.html).
|
|
12
|
+
|
|
13
|
+
### Rule logic
|
|
14
|
+
|
|
15
|
+
Each HTTP client request is expected to have a descendant labeled with the expected label.
|
|
16
|
+
|
|
17
|
+
### Notes
|
|
18
|
+
|
|
19
|
+
Use the circuit breaker pattern in microservices architecture to make system behavior more
|
|
20
|
+
predictable when a service becomes overloaded or unavailable.
|
|
21
|
+
|
|
22
|
+
### Resolution
|
|
23
|
+
|
|
24
|
+
Utilize a circuit breaker library - your organization may have a specific preference.
|
|
25
|
+
|
|
26
|
+
Some examples:
|
|
27
|
+
|
|
28
|
+
- [Hystrix (Java)](https://github.com/Netflix/Hystrix/wiki/How-it-Works#CircuitBreaker)
|
|
29
|
+
- [CircuitBox (Ruby)](https://github.com/yammer/circuitbox)
|
|
30
|
+
- [Semian (Ruby)](https://github.com/Shopify/semian#circuit-breaker)
|
|
31
|
+
- [pybreaker (Python)](https://github.com/danielfm/pybreaker)
|
|
32
|
+
|
|
33
|
+
### Options
|
|
34
|
+
|
|
35
|
+
- `expectedLabel`. Label which identifies the circuit breaker function. Default:
|
|
36
|
+
`rpc.circuit_breaker`.
|
|
37
|
+
|
|
38
|
+
### Examples
|
|
39
|
+
|
|
40
|
+
```yaml
|
|
41
|
+
- rule: rpcWithoutCircuitBreaker
|
|
42
|
+
properties:
|
|
43
|
+
expectedLabel: rpc.circuit_breaker
|
|
44
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule: save-without-validation
|
|
3
|
+
name: Save without validation
|
|
4
|
+
title: Save without validation
|
|
5
|
+
references:
|
|
6
|
+
CWE-20: https://cwe.mitre.org/data/definitions/20.html
|
|
7
|
+
impactDomain: Stability
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
Ensures that data saved by data access object is validated first.
|
|
11
|
+
|
|
12
|
+
### Rule logic
|
|
13
|
+
|
|
14
|
+
Finds events whose method name is `save` or `save!`. Then verifies that each of these events has a
|
|
15
|
+
descendant whose method name is `valid?` or `validate!`.
|
|
16
|
+
|
|
17
|
+
### Notes
|
|
18
|
+
|
|
19
|
+
In a future revision, this rule will be refactored to use labels rather than method names.
|
|
20
|
+
|
|
21
|
+
### Resolution
|
|
22
|
+
|
|
23
|
+
Ensure that data is validated before being saved; for example, using a `before_save` hook.
|
|
24
|
+
|
|
25
|
+
### Options
|
|
26
|
+
|
|
27
|
+
None
|
|
28
|
+
|
|
29
|
+
### Examples
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
- rule: saveWithoutValidation
|
|
33
|
+
```
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule: secret-in-log
|
|
3
|
+
name: Secret in log
|
|
4
|
+
title: Secret in log
|
|
5
|
+
references:
|
|
6
|
+
CWE-532: https://cwe.mitre.org/data/definitions/532.html
|
|
7
|
+
impactDomain: Security
|
|
8
|
+
labels:
|
|
9
|
+
- secret
|
|
10
|
+
- log
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
Identifies when a known or assumed secret is written to a log. Logs are often transported into other
|
|
14
|
+
systems that are treated with lesser security - such as backups. Therefore, secrets written into log
|
|
15
|
+
files are more likely to be leaked or discovered by cyber-attackers.
|
|
16
|
+
|
|
17
|
+
### Rule logic
|
|
18
|
+
|
|
19
|
+
Operation of this rule depends on two labels: `secret` and `log`. A function labeled `secret` is
|
|
20
|
+
assumed to return a secret value - this rule keeps a running tally of all the secrets created in an
|
|
21
|
+
AppMap. When a function labeled `log` is called, the rule looks at the log message, and checks
|
|
22
|
+
whether any of the known secrets are in it.
|
|
23
|
+
|
|
24
|
+
The log message is also tested against a list of known regular expressions that are likely to match
|
|
25
|
+
secrets.
|
|
26
|
+
|
|
27
|
+
### Notes
|
|
28
|
+
|
|
29
|
+
Be sure and apply the `secret` label to any function in your code base that generates a secret (such
|
|
30
|
+
as encryption keys, user tokens, API keys, reset tokens, etc.).
|
|
31
|
+
|
|
32
|
+
### Resolution
|
|
33
|
+
|
|
34
|
+
If the log message is written by code that you control, the simplest resolution is to remove the log
|
|
35
|
+
statement - or to remove the secret from the log message.
|
|
36
|
+
|
|
37
|
+
If the log message is not controlled by you (it's generated by a framework), your choices are more
|
|
38
|
+
limited. You could open a pull request with the framework, or you can change the log level to
|
|
39
|
+
suppress the offending message.
|
|
40
|
+
|
|
41
|
+
### Options
|
|
42
|
+
|
|
43
|
+
None
|
|
44
|
+
|
|
45
|
+
### Examples
|
|
46
|
+
|
|
47
|
+
```yaml
|
|
48
|
+
- rule: secretInLog
|
|
49
|
+
```
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule: slow-function-call
|
|
3
|
+
name: Slow function call
|
|
4
|
+
title: Slow function call
|
|
5
|
+
impactDomain: Performance
|
|
6
|
+
scope: root
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Ensures that function elapsed time does not exceed a threshold.
|
|
10
|
+
|
|
11
|
+
### Rule logic
|
|
12
|
+
|
|
13
|
+
Checks all configured functions to see if the elapsed time exceeds the configured threshold.
|
|
14
|
+
|
|
15
|
+
### Notes
|
|
16
|
+
|
|
17
|
+
This rule is most useful when applied to code that is specifically designed to test application
|
|
18
|
+
performance.
|
|
19
|
+
|
|
20
|
+
### Resolution
|
|
21
|
+
|
|
22
|
+
Optimize the function elapsed time using a profiler, SQL tuning, etc.
|
|
23
|
+
|
|
24
|
+
### Options
|
|
25
|
+
|
|
26
|
+
- `functions: `[MatchPatternConfig](/docs/analysis/match-pattern-config.html)`[]` list of functions
|
|
27
|
+
to check. Required.
|
|
28
|
+
- `timeAllowed` max time (in seconds) allowed for the function.
|
|
29
|
+
|
|
30
|
+
### Examples
|
|
31
|
+
|
|
32
|
+
```yaml
|
|
33
|
+
- rule: slowFunctionCall
|
|
34
|
+
properties:
|
|
35
|
+
timeAllowed: 0.25
|
|
36
|
+
functions:
|
|
37
|
+
- match: ^app/models
|
|
38
|
+
- match: ^app/jobs
|
|
39
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule: slow-http-server-request
|
|
3
|
+
name: Slow HTTP server request
|
|
4
|
+
title: Slow HTTP server request
|
|
5
|
+
impactDomain: Performance
|
|
6
|
+
scope: http_server_request
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Ensures that HTTP server request elapsed time does not exceed a threshold.
|
|
10
|
+
|
|
11
|
+
### Rule logic
|
|
12
|
+
|
|
13
|
+
Checks HTTP server requests to see if the elapsed time exceeds the configured threshold.
|
|
14
|
+
|
|
15
|
+
### Notes
|
|
16
|
+
|
|
17
|
+
This rule is most useful when applied to code that is specifically designed to test application
|
|
18
|
+
performance.
|
|
19
|
+
|
|
20
|
+
### Resolution
|
|
21
|
+
|
|
22
|
+
Optimize the request elapsed time using a profiler, SQL tuning, etc.
|
|
23
|
+
|
|
24
|
+
### Options
|
|
25
|
+
|
|
26
|
+
- `timeAllowed` max time (in seconds) allowed for the request.
|
|
27
|
+
|
|
28
|
+
### Examples
|
|
29
|
+
|
|
30
|
+
```yaml
|
|
31
|
+
- rule: slowHttpServerRequest
|
|
32
|
+
properties:
|
|
33
|
+
timeAllowed: 0.25
|
|
34
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule: slow-query
|
|
3
|
+
name: Slow query
|
|
4
|
+
title: Slow SQL query
|
|
5
|
+
impactDomain: Performance
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Ensures that SQL query elapsed time does not exceed a threshold.
|
|
9
|
+
|
|
10
|
+
### Rule logic
|
|
11
|
+
|
|
12
|
+
Checks all configured queries to see if the elapsed time exceeds the configured threshold.
|
|
13
|
+
|
|
14
|
+
### Notes
|
|
15
|
+
|
|
16
|
+
This rule is most useful when applied to code that is specifically designed to test application
|
|
17
|
+
performance.
|
|
18
|
+
|
|
19
|
+
### Resolution
|
|
20
|
+
|
|
21
|
+
Optimize the elapsed time using SQL tuning.
|
|
22
|
+
|
|
23
|
+
### Options
|
|
24
|
+
|
|
25
|
+
- `timeAllowed` max time (in seconds) allowed for the query.
|
|
26
|
+
|
|
27
|
+
### Examples
|
|
28
|
+
|
|
29
|
+
```yaml
|
|
30
|
+
- rule: slowQuery
|
|
31
|
+
properties:
|
|
32
|
+
timeAllowed: 0.25
|
|
33
|
+
```
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule: too-many-joins
|
|
3
|
+
name: Too many joins
|
|
4
|
+
title: Too many joins
|
|
5
|
+
references:
|
|
6
|
+
CWE-1049: https://cwe.mitre.org/data/definitions/1049.html
|
|
7
|
+
impactDomain: Performance
|
|
8
|
+
scope: command
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Verifies that the number of joins in SQL queries does not exceed a threshold.
|
|
12
|
+
|
|
13
|
+
### Rule logic
|
|
14
|
+
|
|
15
|
+
Counts the number of joins in each SQL query. If the count exceeds the configured threshold, a
|
|
16
|
+
finding is reported.
|
|
17
|
+
|
|
18
|
+
### Notes
|
|
19
|
+
|
|
20
|
+
Queries with too many joins often have poor or unpredictable performance.
|
|
21
|
+
|
|
22
|
+
### Resolution
|
|
23
|
+
|
|
24
|
+
Having multiple joins may be legitimate - but it is a situation that merits a closer look,
|
|
25
|
+
especially when the query is generated by an ORM.
|
|
26
|
+
|
|
27
|
+
Take a look at a query plan from a database that has a realistically high volume of data, and verify
|
|
28
|
+
that the query can be executed efficiently.
|
|
29
|
+
|
|
30
|
+
### Options
|
|
31
|
+
|
|
32
|
+
- `warningLimit` the maximum number of joins allowed.
|
|
33
|
+
|
|
34
|
+
### Examples
|
|
35
|
+
|
|
36
|
+
```yaml
|
|
37
|
+
- rule: tooManyJoins
|
|
38
|
+
properties:
|
|
39
|
+
warningLimit: 5
|
|
40
|
+
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule: too-many-updates
|
|
3
|
+
name: Too many updates
|
|
4
|
+
title: Too many SQL and RPC updates performed in one command
|
|
5
|
+
references:
|
|
6
|
+
CWE-1048: https://cwe.mitre.org/data/definitions/1048.html
|
|
7
|
+
impactDomain: Maintainability
|
|
8
|
+
scope: command
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Verifies that the number of SQL and RPC updates performed by a command does not exceed a threshold.
|
|
12
|
+
|
|
13
|
+
### Rule logic
|
|
14
|
+
|
|
15
|
+
Counts the number of SQL and RPC updates in each command. A SQL update is any `INSERT` or `UPDATE`
|
|
16
|
+
query. An RPC update is an HTTP client request that uses `PUT`, `POST`, or `PATCH`.
|
|
17
|
+
|
|
18
|
+
If the number of updates exceeds the threshold, a finding is reported.
|
|
19
|
+
|
|
20
|
+
### Notes
|
|
21
|
+
|
|
22
|
+
As a codebase evolves, sometimes a request can start to make more and more SQL and RPC updates.There
|
|
23
|
+
are several negative repercussions of this:
|
|
24
|
+
|
|
25
|
+
1. It's no longer clear to a developer what the primary responsibility of the command is.
|
|
26
|
+
2. The command becomes more likely to fail - resulting in a rollback of all the updates, or possibly
|
|
27
|
+
leaving the system in an inconsistent state.
|
|
28
|
+
3. The performance of the command degrades as it does more and more work.
|
|
29
|
+
|
|
30
|
+
### Resolution
|
|
31
|
+
|
|
32
|
+
Consider refactoring the command into multiple commands.
|
|
33
|
+
|
|
34
|
+
Schedule a job to perform some of the work offline.
|
|
35
|
+
|
|
36
|
+
### Options
|
|
37
|
+
|
|
38
|
+
- `warningLimit` the maximum number of joins allowed.
|
|
39
|
+
|
|
40
|
+
### Examples
|
|
41
|
+
|
|
42
|
+
```yaml
|
|
43
|
+
- rule: tooManyUpdates
|
|
44
|
+
properties:
|
|
45
|
+
warningLimit: 20
|
|
46
|
+
```
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule: unbatched-materialized-query
|
|
3
|
+
name: Unbatched materialized query
|
|
4
|
+
title: Unbatched materialized SQL query
|
|
5
|
+
references:
|
|
6
|
+
CWE-1049: https://cwe.mitre.org/data/definitions/1049.html
|
|
7
|
+
impactDomain: Performance
|
|
8
|
+
labels:
|
|
9
|
+
- dao.materialize
|
|
10
|
+
scope: command
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
Finds large data sets that are queried from the database and loaded into memory.
|
|
14
|
+
|
|
15
|
+
### Rule logic
|
|
16
|
+
|
|
17
|
+
Examines all SQL SELECT queries (as opposed to insert/update). If the query satisfies any of the
|
|
18
|
+
following conditions:
|
|
19
|
+
|
|
20
|
+
- Has a LIMIT clause
|
|
21
|
+
- Has a COUNT clause at the top level
|
|
22
|
+
- Queries only for metadata (`sqlite_master` table)
|
|
23
|
+
|
|
24
|
+
then the query is skipped.
|
|
25
|
+
|
|
26
|
+
Otherwise, the rule checks to see if the query has an ancestor labeled `dao.materialize`. If so,
|
|
27
|
+
it's emitted as a finding.
|
|
28
|
+
|
|
29
|
+
### Notes
|
|
30
|
+
|
|
31
|
+
Materializing large amounts of data code objects is a frequent cause of poor performance and memory
|
|
32
|
+
exhaustion.
|
|
33
|
+
|
|
34
|
+
A `COUNT` or `LIMIT` clause is a good indication that the code is taking steps to limit the amount
|
|
35
|
+
of data that's loaded into memory.
|
|
36
|
+
|
|
37
|
+
### Resolution
|
|
38
|
+
|
|
39
|
+
If data is being loaded into memory from an un-LIMITed query:
|
|
40
|
+
|
|
41
|
+
- Consider whether the data processing that's being performed in-memory can be performed in the
|
|
42
|
+
database - either via SQL or a stored procedure.
|
|
43
|
+
- If the data is being loaded solely for presentation purposes, fetch data in batches - e.g. with a
|
|
44
|
+
pagination library.
|
|
45
|
+
|
|
46
|
+
### Options
|
|
47
|
+
|
|
48
|
+
None
|
|
49
|
+
|
|
50
|
+
### Examples
|
|
51
|
+
|
|
52
|
+
```yaml
|
|
53
|
+
- rule: unbatchedMaterializedQuery
|
|
54
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule: update-in-get-request
|
|
3
|
+
name: Update in get request
|
|
4
|
+
title: Data update performed in GET or HEAD request
|
|
5
|
+
impactDomain: Maintainability
|
|
6
|
+
labels:
|
|
7
|
+
- audit
|
|
8
|
+
scope: http_server_request
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Finds SQL updates that are performed in an HTTP server `GET` request.
|
|
12
|
+
|
|
13
|
+
### Rule logic
|
|
14
|
+
|
|
15
|
+
Checks each HTTP server `GET` and `HEAD` request. Within each of these requests, checks for SQL
|
|
16
|
+
queries that match the `queryInclude` option and don't match `queryExclude`. If any such queries
|
|
17
|
+
exist, they are emitted as findings.
|
|
18
|
+
|
|
19
|
+
### Notes
|
|
20
|
+
|
|
21
|
+
Performing data updates in a `GET` request is anti-pattern, and counter to the intent of HTTP. For
|
|
22
|
+
data modifications, use `PUT`, `POST`, or `PATCH`.
|
|
23
|
+
|
|
24
|
+
Data updates which are used for the purposes of tracking user activity are fine in any type of
|
|
25
|
+
request. Use the `queryExclude` option to allow these queries.
|
|
26
|
+
|
|
27
|
+
### Resolution
|
|
28
|
+
|
|
29
|
+
Perform data updates in `PUT`, `POST`, or `PATCH` requests.
|
|
30
|
+
|
|
31
|
+
### Options
|
|
32
|
+
|
|
33
|
+
- `queryInclude: RegExp[]`. Default: `[/\binsert\b/i, /\bupdate\b/i]`.
|
|
34
|
+
- `queryExclude: RegExp[]`. Default: empty.
|
|
35
|
+
|
|
36
|
+
### Examples
|
|
37
|
+
|
|
38
|
+
```yaml
|
|
39
|
+
- rule: updateInGetRequest
|
|
40
|
+
properties:
|
|
41
|
+
queryExclude:
|
|
42
|
+
- /\bINSERT\b/i
|
|
43
|
+
- /\bUPDATE\b/i
|
|
44
|
+
```
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appland/scanner",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.44.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"bin": "built/cli.js",
|
|
6
6
|
"files": [
|
|
7
|
-
"built"
|
|
7
|
+
"built",
|
|
8
|
+
"doc"
|
|
8
9
|
],
|
|
9
10
|
"scripts": {
|
|
10
11
|
"build": "mkdir -p built && cp -r src/sampleConfig built && tsc && yarn schema && yarn doc",
|
|
@@ -38,9 +39,9 @@
|
|
|
38
39
|
"jest": "^27.4.7",
|
|
39
40
|
"nock": "^13.2.2",
|
|
40
41
|
"openapi-types": "^9.3.0",
|
|
41
|
-
"pkg": "^5.5.
|
|
42
|
+
"pkg": "^5.5.2",
|
|
42
43
|
"prettier": "^2.3.2",
|
|
43
|
-
"semantic-release": "^
|
|
44
|
+
"semantic-release": "^19.0.2",
|
|
44
45
|
"sinon": "^11.1.2",
|
|
45
46
|
"ts-jest": "^27.1.3",
|
|
46
47
|
"ts-json-schema-generator": "^0.97.0",
|
|
@@ -49,8 +50,10 @@
|
|
|
49
50
|
},
|
|
50
51
|
"dependencies": {
|
|
51
52
|
"@appland/client": "^1.1.3",
|
|
52
|
-
"@appland/models": "^1.
|
|
53
|
+
"@appland/models": "^1.12.1",
|
|
54
|
+
"@appland/sql-parser": "^1.3.0",
|
|
53
55
|
"@types/async": "^3.2.12",
|
|
56
|
+
"@types/lru-cache": "^5.1.1",
|
|
54
57
|
"@types/sinon": "^10.0.2",
|
|
55
58
|
"@types/tar-stream": "^2.2.2",
|
|
56
59
|
"ajv": "^8.8.2",
|
|
@@ -59,10 +62,11 @@
|
|
|
59
62
|
"chalk": "^4.1.2",
|
|
60
63
|
"form-data": "^4.0.0",
|
|
61
64
|
"js-yaml": "^4.1.0",
|
|
65
|
+
"lru-cache": "^6.0.0",
|
|
62
66
|
"minimatch": "^3.0.4",
|
|
63
67
|
"octokat": "^0.10.0",
|
|
64
68
|
"openapi-diff": "^0.23.5",
|
|
65
|
-
"
|
|
69
|
+
"pretty-format": "^27.4.6",
|
|
66
70
|
"supports-hyperlinks": "^2.2.0",
|
|
67
71
|
"tar-stream": "^2.2.0",
|
|
68
72
|
"yargs": "^17.1.1"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"appMap.js","sourceRoot":"","sources":["../../../src/integration/appland/appMap.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,gDAAqE;AACrE,wDAAiC;AAUjC;IAAA;IAuCA,CAAC;IAtCc,aAAM,GAAnB,UAAoB,IAAY,EAAE,OAA2B;QAA3B,wBAAA,EAAA,YAA2B;;;;;;wBACrD,IAAI,GAAG,IAAI,mBAAQ,EAAE,CAAC;wBAC5B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;wBACrC,IAAI,OAAO,CAAC,GAAG,EAAE;4BACf,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;yBACjC;wBAEe,qBAAM,IAAA,kBAAY,EAAC,aAAa,CAAC,EAAA;;wBAA3C,OAAO,GAAG,SAAiC;wBACjD,sBAAO,IAAI,OAAO,CAAkB,UAAC,OAAO,EAAE,MAAM;gCAClD,IAAM,GAAG,GAAG,OAAO,CAAC,eAAe,CACjC,OAAO,CAAC,GAAG,EACX;oCACE,MAAM,EAAE,MAAM;oCACd,OAAO,wBACF,OAAO,CAAC,OAAO,GACf,IAAI,CAAC,UAAU,EAAE,CACrB;iCACF,EACD,OAAO,CACR,CAAC;gCACF,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gCACxB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;4BACjB,CAAC,CAAC;iCACC,IAAI,CAAC,iBAAW,CAAC;iCACjB,IAAI,CAAC,UAAC,QAAyB;gCAC9B,OAAO,IAAI,OAAO,CAAuB,UAAC,OAAO,EAAE,MAAM;oCACvD,IAAM,YAAY,GAAa,EAAE,CAAC;oCAClC,QAAQ;yCACL,EAAE,CAAC,MAAM,EAAE,UAAC,KAAa;wCACxB,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;oCACxC,CAAC,CAAC;yCACD,EAAE,CAAC,KAAK,EAAE;wCACT,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAyB,CAAC,CAAC;oCACtF,CAAC,CAAC;yCACD,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gCACzB,CAAC,CAAC,CAAC;4BACL,CAAC,CAAC,EAAC;;;;KACN;IACH,aAAC;AAAD,CAAC,AAvCD,IAuCC;AAvCY,wBAAM"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fetchStatus.js","sourceRoot":"","sources":["../../../src/integration/appland/fetchStatus.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gDAAsE;AAEtE,mBAA+B,KAAa;;;;wBACnC,qBAAM,IAAI,SAAG,CAAC,KAAK,CAAC,CAAC,iBAAiB,EAAE,EAAA;wBAA/C,sBAAO,SAAwC,EAAC;;;;CACjD;AAFD,4BAEC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mapset.js","sourceRoot":"","sources":["../../../src/integration/appland/mapset.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,gDAAqE;AAsBrE;IAAA;IA8CA,CAAC;IA7Cc,aAAM,GAAnB,UACE,KAAa,EACb,SAAmB,EACnB,OAA2B;QAA3B,wBAAA,EAAA,YAA2B;;;;;;wBAE3B,OAAO,CAAC,GAAG,CAAC,4BAA0B,KAAK,cAAS,SAAS,CAAC,MAAM,aAAU,CAAC,CAAC;wBAE1E,OAAO,GAAG,IAAI,CAAC,SAAS,YAC5B,GAAG,EAAE,KAAK,EACV,OAAO,EAAE,SAAS,IACf,OAAO,EACV,CAAC;wBACa,qBAAM,IAAA,kBAAY,EAAC,aAAa,CAAC,EAAA;;wBAA3C,OAAO,GAAG,SAAiC;wBACjD,sBAAO,IAAI,OAAO,CAAkB,UAAC,OAAO,EAAE,MAAM;gCAClD,IAAM,GAAG,GAAG,OAAO,CAAC,eAAe,CACjC,OAAO,CAAC,GAAG,EACX;oCACE,MAAM,EAAE,MAAM;oCACd,OAAO,aACL,cAAc,EAAE,kBAAkB,EAClC,gBAAgB,EAAE,OAAO,CAAC,MAAM,IAC7B,OAAO,CAAC,OAAO,CACnB;iCACF,EACD,OAAO,CACR,CAAC;gCACF,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gCACxB,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gCACnB,GAAG,CAAC,GAAG,EAAE,CAAC;4BACZ,CAAC,CAAC;iCACC,IAAI,CAAC,iBAAW,CAAC;iCACjB,IAAI,CAAC,UAAC,QAAyB;gCAC9B,OAAO,IAAI,OAAO,CAAiB,UAAC,OAAO,EAAE,MAAM;oCACjD,IAAM,YAAY,GAAa,EAAE,CAAC;oCAClC,QAAQ;yCACL,EAAE,CAAC,MAAM,EAAE,UAAC,KAAa;wCACxB,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;oCACxC,CAAC,CAAC;yCACD,EAAE,CAAC,KAAK,EAAE;wCACT,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAmB,CAAC,CAAC;oCAChF,CAAC,CAAC;yCACD,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gCACzB,CAAC,CAAC,CAAC;4BACL,CAAC,CAAC,EAAC;;;;KACN;IACH,aAAC;AAAD,CAAC,AA9CD,IA8CC;AA9CY,wBAAM"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"upload.js","sourceRoot":"","sources":["../../../src/integration/appland/upload.ts"],"names":[],"mappings":"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"}
|