deepsecrets 2.1.0__tar.gz → 2.2.0__tar.gz
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.
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/PKG-INFO +15 -6
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/README.md +12 -2
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/cli.py +194 -124
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/config.py +39 -7
- deepsecrets-2.2.0/deepsecrets/core/engines/regex.py +88 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/engines/semantic.py +14 -2
- deepsecrets-2.2.0/deepsecrets/core/helpers/confidence.py +18 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/helpers/content_analyzer.py +2 -2
- deepsecrets-2.2.0/deepsecrets/core/helpers/encoding.py +151 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/helpers/naturalness_scorer.py +17 -10
- deepsecrets-2.2.0/deepsecrets/core/helpers/regex_candidate_evaluator.py +72 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/helpers/variable_evaluator.py +27 -13
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/model/finding.py +15 -3
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/model/internal/processing.py +10 -0
- deepsecrets-2.2.0/deepsecrets/core/model/regex_candidate.py +95 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/model/response/dojo_sarif.py +35 -44
- deepsecrets-2.2.0/deepsecrets/core/model/rules/regex.py +195 -0
- deepsecrets-2.2.0/deepsecrets/core/model/rules/regex_candidate_scoring.py +142 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/model/rules/rule.py +5 -2
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/model/rules/variable_scoring.py +8 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/modes/iscan_mode.py +239 -29
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/rulesets/ibuilder.py +1 -0
- deepsecrets-2.2.0/deepsecrets/core/rulesets/regex_candidate_scoring.py +7 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/tokenizers/cheap_var_search.py +10 -1
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/tokenizers/helpers/semantic/deep_analyzer.py +36 -5
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/tokenizers/helpers/semantic/var_detection/detector.py +3 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/tokenizers/helpers/semantic/var_detection/rules.py +46 -4
- deepsecrets-2.2.0/deepsecrets/core/tokenizers/helpers/single_token_improver.py +88 -0
- deepsecrets-2.2.0/deepsecrets/core/tokenizers/helpers/token_table.py +99 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/tokenizers/helpers/type_stream.py +49 -1
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/tokenizers/lexer.py +37 -28
- deepsecrets-2.2.0/deepsecrets/core/utils/environment.py +37 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/utils/file_analyzer.py +16 -4
- deepsecrets-2.2.0/deepsecrets/core/utils/gc_freeze.py +7 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/utils/lexer_finder.py +4 -6
- deepsecrets-2.2.0/deepsecrets/core/utils/multiprocessing_setup.py +142 -0
- deepsecrets-2.2.0/deepsecrets/rules/excluded_bundles.json +5 -0
- deepsecrets-2.2.0/deepsecrets/rules/regex_candidate_scoring_rules.json +180 -0
- deepsecrets-2.2.0/deepsecrets/rules/regexes.json +571 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/rules/variable_scoring_rules.json +8 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/scan_modes/cli.py +36 -7
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/pyproject.toml +7 -5
- deepsecrets-2.1.0/deepsecrets/core/engines/regex.py +0 -39
- deepsecrets-2.1.0/deepsecrets/core/model/rules/regex.py +0 -118
- deepsecrets-2.1.0/deepsecrets/core/tokenizers/helpers/single_token_improver.py +0 -104
- deepsecrets-2.1.0/deepsecrets/diagnostics/__init__.py +0 -5
- deepsecrets-2.1.0/deepsecrets/diagnostics/__main__.py +0 -62
- deepsecrets-2.1.0/deepsecrets/diagnostics/trace.py +0 -405
- deepsecrets-2.1.0/deepsecrets/diagnostics/worker.py +0 -39
- deepsecrets-2.1.0/deepsecrets/rules/regexes.json +0 -237
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/LICENSE +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/__init__.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/__main__.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/engines/__init__.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/engines/hashed_secret.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/engines/iengine.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/helpers/entropy.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/model/__init__.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/model/file.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/model/response/base.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/model/response/builtin.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/model/rules/__init__.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/model/rules/exlcuded_path.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/model/rules/false_finding.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/model/rules/hashed_secret.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/model/rules/hashing.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/model/semantic.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/model/token.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/model/tokenized_region.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/rulesets/excluded_paths.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/rulesets/false_findings.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/rulesets/hashed_secrets.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/rulesets/regex.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/rulesets/variable_scoring.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/tokenizers/__init__.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/tokenizers/full_content.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/tokenizers/helpers/__init__.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/tokenizers/helpers/cheap_var_search/__init__.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/tokenizers/helpers/semantic/__init__.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/tokenizers/helpers/semantic/language.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/tokenizers/helpers/semantic/var_detection/__init__.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/tokenizers/helpers/subfile_regions_helper.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/tokenizers/itokenizer.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/tokenizers/per_line.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/tokenizers/per_word.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/ui/floating_header.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/ui/progress_bar.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/ui/time_remaining_column.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/utils/__init__.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/utils/cpu.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/utils/exceptions.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/utils/finding_merger.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/utils/fs.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/utils/guess_filetype.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/utils/hashing.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/utils/lifecycle_hooks.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/utils/log.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/utils/progress.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/core/utils/string.py +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/rules/excluded_paths.json +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/rules/value_scoring_model.json +0 -0
- {deepsecrets-2.1.0 → deepsecrets-2.2.0}/deepsecrets/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepsecrets
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: A better tool for secrets scanning
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -9,7 +9,7 @@ Author: Nikolai Khechumov
|
|
|
9
9
|
Author-email: khechumov@gmail.com
|
|
10
10
|
Maintainer: Nikolai Khechumov
|
|
11
11
|
Maintainer-email: khechumov@gmail.com
|
|
12
|
-
Requires-Python: >=3.
|
|
12
|
+
Requires-Python: >=3.11,<4.0.0
|
|
13
13
|
Classifier: Programming Language :: Python :: 3
|
|
14
14
|
Classifier: License :: OSI Approved :: MIT License
|
|
15
15
|
Classifier: Operating System :: OS Independent
|
|
@@ -17,7 +17,6 @@ Classifier: Environment :: Console
|
|
|
17
17
|
Classifier: Topic :: Security
|
|
18
18
|
Requires-Dist: aenum (==3.1.17)
|
|
19
19
|
Requires-Dist: jschema-to-python (==1.2.3)
|
|
20
|
-
Requires-Dist: jsx-lexer (==2.0.1)
|
|
21
20
|
Requires-Dist: mmh3 (==5.3.0)
|
|
22
21
|
Requires-Dist: ordered-set (==4.1.0)
|
|
23
22
|
Requires-Dist: puppetparser (==0.2.14)
|
|
@@ -28,11 +27,12 @@ Requires-Dist: regex (==2026.9.3)
|
|
|
28
27
|
Requires-Dist: rich (==15.0.0)
|
|
29
28
|
Requires-Dist: sarif-om (==1.0.4)
|
|
30
29
|
Project-URL: Bug Tracker, https://github.com/ntoskernel/deepsecrets/issues
|
|
31
|
-
Project-URL: Homepage, https://owasp.org/
|
|
30
|
+
Project-URL: Homepage, https://owasp.org/projects/deepsecrets
|
|
32
31
|
Project-URL: Repository, https://github.com/ntoskernel/deepsecrets
|
|
33
32
|
Description-Content-Type: text/markdown
|
|
34
33
|
|
|
35
|
-
# DeepSecrets
|
|
34
|
+
# [OWASP](https://owasp.org/projects/deepsecrets) DeepSecrets — a better tool for secrets scanning
|
|
35
|
+
|
|
36
36
|
|
|
37
37
|

|
|
38
38
|
|
|
@@ -78,6 +78,8 @@ From PyPi
|
|
|
78
78
|
$ pip install deepsecrets
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
+
DeepSecrets needs Python 3.11 or later.
|
|
82
|
+
|
|
81
83
|
|
|
82
84
|
## Scanning
|
|
83
85
|
The easiest way to run a scan:
|
|
@@ -97,7 +99,14 @@ The `--help` command is always ready to guide you, but here are the key flags yo
|
|
|
97
99
|
* `--regex-rules /path/to/rules.json`: Supply your own custom regex ruleset.
|
|
98
100
|
* `--hashed-values /path/to/hashes.json`: Provide a list of pre-hashed known production secrets to search for them securely.
|
|
99
101
|
* `--excluded-paths /path/to/exclusions.json`: Override or extend the default paths ignored during scanning.
|
|
102
|
+
* `--deep-max-size BYTES`: Files larger than this (default 250,000 bytes) get the fast analysis: the regex rules and the variable search, without the language lexer. Big files are mostly generated or minified, the lexer costs most of the scan time on them and rarely finds more there. `0` gives every file the full analysis.
|
|
103
|
+
* `--skip-bundles`: Skip minified JavaScript, source maps and bundles (`*.min.js`, `*.map`, `*.bundle.js`) entirely, on top of the other path exclusions.
|
|
104
|
+
* `--confidence-level {all,low,medium,high,very-high}`: The lowest confidence to report (default `low`, every finding). `medium`, `high` and `very-high` keep confidence 3, 6 and 9 or more; `all` also reports the regex matches the scanner judged not to be secrets (placeholders, examples), with an `-INFO` rule id.
|
|
105
|
+
* `--report-diagnostics`: Add every file found under the target to the SARIF report, with its status, scan time, skip reason and analysis depth.
|
|
106
|
+
* `--regex-candidate-scoring-rules /path/to/rules.json`: Supply your own rules for judging regex matches before they are reported. Give the flag no value to report regex matches unjudged.
|
|
107
|
+
* `--multiprocessing-context {forkserver,spawn,fork}`: How worker processes start. The default is `forkserver` where the platform has it (Linux, macOS): workers share the scanner already loaded in a server process. Windows uses `spawn`. All three give the same findings.
|
|
100
108
|
* `--disable-masking`: Keep potential secrets unmasked in the output report *(see caution below)*.
|
|
109
|
+
* `--ci` / `--no-ci`: Turn CI mode on or off. CI mode prints a plain progress line every 30 seconds instead of the live progress display. It turns on by itself inside CI services (GitHub Actions, GitLab CI, Jenkins and others) and whenever the output is not a terminal.
|
|
101
110
|
|
|
102
111
|
|
|
103
112
|
### Github Actions Integration
|
|
@@ -146,7 +155,7 @@ Masking doesn't break the deduplication logic of downstream platforms (like Gith
|
|
|
146
155
|
|
|
147
156
|
Every finding gets a confidence score. However, different security platforms parse SARIF metrics differently. To ensure compatibility across modern ASPM dashboards, DeepSecrets does the following:
|
|
148
157
|
|
|
149
|
-
* **Virtual Subrules (`rules[]`)**: Dynamically generates rules
|
|
158
|
+
* **Virtual Subrules (`rules[]`)**: Dynamically generates rules by confidence tier, from `S105-LOW` through `-MEDIUM` and `-HIGH` to `S105-VERY-HIGH` (and `-INFO` for the rejected matches `--confidence-level all` reports). This forces GitHub Security and DefectDojo to map semantic precision variance properly without breaking native parsers.
|
|
150
159
|
|
|
151
160
|
* **Deterministic Result Level**: The tool always explicitly sets `level: error` in the `results[]` model. This acts as a universal fallback for CI/CD pipelines and older SAST parsers, ensuring that exposed secrets reliably break builds or block Pull Requests regardless of individual rule interpretations.
|
|
152
161
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
# DeepSecrets
|
|
1
|
+
# [OWASP](https://owasp.org/projects/deepsecrets) DeepSecrets — a better tool for secrets scanning
|
|
2
|
+
|
|
2
3
|
|
|
3
4
|

|
|
4
5
|
|
|
@@ -44,6 +45,8 @@ From PyPi
|
|
|
44
45
|
$ pip install deepsecrets
|
|
45
46
|
```
|
|
46
47
|
|
|
48
|
+
DeepSecrets needs Python 3.11 or later.
|
|
49
|
+
|
|
47
50
|
|
|
48
51
|
## Scanning
|
|
49
52
|
The easiest way to run a scan:
|
|
@@ -63,7 +66,14 @@ The `--help` command is always ready to guide you, but here are the key flags yo
|
|
|
63
66
|
* `--regex-rules /path/to/rules.json`: Supply your own custom regex ruleset.
|
|
64
67
|
* `--hashed-values /path/to/hashes.json`: Provide a list of pre-hashed known production secrets to search for them securely.
|
|
65
68
|
* `--excluded-paths /path/to/exclusions.json`: Override or extend the default paths ignored during scanning.
|
|
69
|
+
* `--deep-max-size BYTES`: Files larger than this (default 250,000 bytes) get the fast analysis: the regex rules and the variable search, without the language lexer. Big files are mostly generated or minified, the lexer costs most of the scan time on them and rarely finds more there. `0` gives every file the full analysis.
|
|
70
|
+
* `--skip-bundles`: Skip minified JavaScript, source maps and bundles (`*.min.js`, `*.map`, `*.bundle.js`) entirely, on top of the other path exclusions.
|
|
71
|
+
* `--confidence-level {all,low,medium,high,very-high}`: The lowest confidence to report (default `low`, every finding). `medium`, `high` and `very-high` keep confidence 3, 6 and 9 or more; `all` also reports the regex matches the scanner judged not to be secrets (placeholders, examples), with an `-INFO` rule id.
|
|
72
|
+
* `--report-diagnostics`: Add every file found under the target to the SARIF report, with its status, scan time, skip reason and analysis depth.
|
|
73
|
+
* `--regex-candidate-scoring-rules /path/to/rules.json`: Supply your own rules for judging regex matches before they are reported. Give the flag no value to report regex matches unjudged.
|
|
74
|
+
* `--multiprocessing-context {forkserver,spawn,fork}`: How worker processes start. The default is `forkserver` where the platform has it (Linux, macOS): workers share the scanner already loaded in a server process. Windows uses `spawn`. All three give the same findings.
|
|
66
75
|
* `--disable-masking`: Keep potential secrets unmasked in the output report *(see caution below)*.
|
|
76
|
+
* `--ci` / `--no-ci`: Turn CI mode on or off. CI mode prints a plain progress line every 30 seconds instead of the live progress display. It turns on by itself inside CI services (GitHub Actions, GitLab CI, Jenkins and others) and whenever the output is not a terminal.
|
|
67
77
|
|
|
68
78
|
|
|
69
79
|
### Github Actions Integration
|
|
@@ -112,7 +122,7 @@ Masking doesn't break the deduplication logic of downstream platforms (like Gith
|
|
|
112
122
|
|
|
113
123
|
Every finding gets a confidence score. However, different security platforms parse SARIF metrics differently. To ensure compatibility across modern ASPM dashboards, DeepSecrets does the following:
|
|
114
124
|
|
|
115
|
-
* **Virtual Subrules (`rules[]`)**: Dynamically generates rules
|
|
125
|
+
* **Virtual Subrules (`rules[]`)**: Dynamically generates rules by confidence tier, from `S105-LOW` through `-MEDIUM` and `-HIGH` to `S105-VERY-HIGH` (and `-INFO` for the rejected matches `--confidence-level all` reports). This forces GitHub Security and DefectDojo to map semantic precision variance properly without breaking native parsers.
|
|
116
126
|
|
|
117
127
|
* **Deterministic Result Level**: The tool always explicitly sets `level: error` in the `results[]` model. This acts as a universal fallback for CI/CD pipelines and older SAST parsers, ensuring that exposed secrets reliably break builds or block Pull Requests regardless of individual rule interpretations.
|
|
118
128
|
|
|
@@ -1,27 +1,39 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
from datetime import datetime
|
|
3
|
-
import
|
|
3
|
+
import os
|
|
4
4
|
from argparse import RawTextHelpFormatter
|
|
5
5
|
from typing import Dict, List
|
|
6
6
|
from jschema_to_python.to_json import to_json
|
|
7
|
+
from multiprocessing import get_all_start_methods
|
|
7
8
|
|
|
8
9
|
from deepsecrets import MODULE_NAME, console
|
|
9
|
-
from deepsecrets.config import
|
|
10
|
+
from deepsecrets.config import (
|
|
11
|
+
CONFIDENCE_LEVELS,
|
|
12
|
+
DEFAULT_CONFIDENCE_LEVEL,
|
|
13
|
+
DEFAULT_DEEP_MAX_SIZE,
|
|
14
|
+
SCANNER_VERSION,
|
|
15
|
+
Config,
|
|
16
|
+
Output,
|
|
17
|
+
)
|
|
10
18
|
from deepsecrets.core.engines.hashed_secret import HashedSecretEngine
|
|
11
19
|
from deepsecrets.core.engines.regex import RegexEngine
|
|
12
20
|
from deepsecrets.core.engines.semantic import SemanticEngine
|
|
13
21
|
from deepsecrets.core.model.finding import Finding
|
|
14
|
-
from deepsecrets.core.model.response.builtin import BuiltinFormatResponseBuilder
|
|
15
22
|
from deepsecrets.core.model.response.dojo_sarif import DojoSarifResponseBuilder
|
|
16
23
|
from deepsecrets.core.rulesets.false_findings import FalseFindingsBuilder
|
|
17
24
|
from deepsecrets.core.rulesets.hashed_secrets import HashedSecretsRulesetBuilder
|
|
18
25
|
from deepsecrets.core.rulesets.regex import RegexRulesetBuilder
|
|
26
|
+
from deepsecrets.core.rulesets.regex_candidate_scoring import RegexCandidateScoringRulesetBuilder
|
|
19
27
|
from deepsecrets.core.rulesets.variable_scoring import VariableScoringRulesetBuilder
|
|
20
28
|
from deepsecrets.core.ui.progress_bar import DSApplicationProgess
|
|
21
29
|
from deepsecrets.core.ui.time_remaining_column import SyncedTimeRemainingColumn
|
|
30
|
+
from deepsecrets.core.utils.exceptions import FileNotFoundException
|
|
22
31
|
from deepsecrets.core.utils.fs import get_abspath, get_path_inside_package
|
|
23
32
|
from deepsecrets.core.utils.log import logger
|
|
24
33
|
from deepsecrets.scan_modes.cli import CliScanMode
|
|
34
|
+
from deepsecrets.core.modes.iscan_mode import WorkerStartupError
|
|
35
|
+
from deepsecrets.core.utils.environment import is_ci_environment
|
|
36
|
+
from deepsecrets.core.utils.multiprocessing_setup import TempDirTooLongError, default_start_method, stop_forkserver
|
|
25
37
|
|
|
26
38
|
from rich.progress import SpinnerColumn, TextColumn, BarColumn, TaskProgressColumn
|
|
27
39
|
from rich.panel import Panel
|
|
@@ -59,6 +71,8 @@ overall_time_column.progress_instance = progress_bar
|
|
|
59
71
|
|
|
60
72
|
class DeepSecretsCliTool:
|
|
61
73
|
argparser: argparse.ArgumentParser
|
|
74
|
+
# built by parse_arguments, one per parse
|
|
75
|
+
config: Config
|
|
62
76
|
|
|
63
77
|
def __init__(self, args: List[str]):
|
|
64
78
|
self.args = args
|
|
@@ -67,9 +81,6 @@ class DeepSecretsCliTool:
|
|
|
67
81
|
def say_hello(self) -> None:
|
|
68
82
|
console.line(2)
|
|
69
83
|
console.rule('DeepSecrets', characters='=')
|
|
70
|
-
|
|
71
|
-
from rich.panel import Panel
|
|
72
|
-
|
|
73
84
|
console.print(
|
|
74
85
|
Align(
|
|
75
86
|
Panel.fit(
|
|
@@ -109,11 +120,25 @@ class DeepSecretsCliTool:
|
|
|
109
120
|
default=['built-in'],
|
|
110
121
|
)
|
|
111
122
|
|
|
123
|
+
parser.add_argument(
|
|
124
|
+
'--regex-candidate-scoring-rules',
|
|
125
|
+
nargs='*',
|
|
126
|
+
type=str,
|
|
127
|
+
help='Controls rules for judging regex matches (candidates) before they are reported: placeholders,\n'
|
|
128
|
+
'language-like values, hosts, test paths. Used by regex rules that declare an "evidence" class.\n'
|
|
129
|
+
'- Ignore this argument to use the built-in ruleset\n'
|
|
130
|
+
"- Using your own rulesets disables the default one. Add 'built-in' to the args list to merge rulesets\n"
|
|
131
|
+
'eq. --regex-candidate-scoring-rules built-in /root/my_candidate_rules.json\n'
|
|
132
|
+
'- Give it no value to report regex matches unjudged, as before 2.2\n',
|
|
133
|
+
default=['built-in'],
|
|
134
|
+
)
|
|
135
|
+
|
|
112
136
|
parser.add_argument(
|
|
113
137
|
'--hashed-values',
|
|
114
138
|
nargs='*',
|
|
115
139
|
type=str,
|
|
116
|
-
help='Path to your Hashed Values set.\n'
|
|
140
|
+
help='Path to your Hashed Values set.\n'
|
|
141
|
+
'Leave the flag out, or give it no value, to disable these checks\n',
|
|
117
142
|
)
|
|
118
143
|
|
|
119
144
|
parser.add_argument(
|
|
@@ -150,6 +175,34 @@ class DeepSecretsCliTool:
|
|
|
150
175
|
default=['built-in'],
|
|
151
176
|
)
|
|
152
177
|
|
|
178
|
+
parser.add_argument(
|
|
179
|
+
'--skip-bundles',
|
|
180
|
+
action='store_true',
|
|
181
|
+
help='Also skip minified JavaScript, source maps and bundles (*.min.js, *.map, *.bundle.js), whatever\n'
|
|
182
|
+
"--excluded-paths lists (except 'disable'). Most of them are larger than --deep-max-size and get the\n"
|
|
183
|
+
'fast analysis anyway.\n',
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
parser.add_argument(
|
|
187
|
+
'--deep-max-size',
|
|
188
|
+
type=int,
|
|
189
|
+
default=DEFAULT_DEEP_MAX_SIZE,
|
|
190
|
+
help='Files larger than this many bytes get the fast analysis: the regex rules and the variable search,\n'
|
|
191
|
+
'without the language lexer, which costs most of the scan time on big files and rarely finds more there.\n'
|
|
192
|
+
f'Default: {DEFAULT_DEEP_MAX_SIZE}. 0 gives every file the full analysis.\n',
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
parser.add_argument(
|
|
196
|
+
'--confidence-level',
|
|
197
|
+
type=str,
|
|
198
|
+
default=DEFAULT_CONFIDENCE_LEVEL,
|
|
199
|
+
choices=list(CONFIDENCE_LEVELS),
|
|
200
|
+
help='The lowest confidence to report, from all to very-high (default: low).\n'
|
|
201
|
+
'"low" reports every finding; "medium", "high" and "very-high" keep confidence 3, 6 and 9 or more.\n'
|
|
202
|
+
'"all" also reports the regex matches the scanner judged not to be secrets (placeholders, examples),\n'
|
|
203
|
+
'at confidence 0 with an -INFO rule id.\n',
|
|
204
|
+
)
|
|
205
|
+
|
|
153
206
|
parser.add_argument(
|
|
154
207
|
'--false-findings',
|
|
155
208
|
nargs='*',
|
|
@@ -195,9 +248,20 @@ class DeepSecretsCliTool:
|
|
|
195
248
|
parser.add_argument(
|
|
196
249
|
'--multiprocessing-context',
|
|
197
250
|
type=str,
|
|
198
|
-
default=
|
|
199
|
-
|
|
200
|
-
|
|
251
|
+
default=None,
|
|
252
|
+
# the platform's own: fork and forkserver do not exist on Windows
|
|
253
|
+
choices=get_all_start_methods(),
|
|
254
|
+
help='How worker processes start. Default: forkserver where available (Linux, macOS), spawn elsewhere.\n'
|
|
255
|
+
'forkserver workers share the scanner already loaded in the server instead of loading it again.\n',
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
parser.add_argument(
|
|
259
|
+
'--ci',
|
|
260
|
+
action=argparse.BooleanOptionalAction,
|
|
261
|
+
default=None,
|
|
262
|
+
help='CI mode: no live progress bars, a plain progress line every 30 s, and no progress manager process.\n'
|
|
263
|
+
'Default: on inside a CI service (CI, GITHUB_ACTIONS, GITLAB_CI and similar variables) or when the\n'
|
|
264
|
+
'output is not a terminal; --no-ci forces the live display.\n',
|
|
201
265
|
)
|
|
202
266
|
|
|
203
267
|
parser.add_argument('--outfile', required=True, type=str)
|
|
@@ -208,7 +272,7 @@ class DeepSecretsCliTool:
|
|
|
208
272
|
choices=['json', 'sarif', 'dojo-sarif'],
|
|
209
273
|
help='"sarif": SARIF format (specification accurate, default)\n'
|
|
210
274
|
'"dojo-sarif": SARIF format (compatible with DefectDojo\'s parser)\n'
|
|
211
|
-
'"json": old internal format
|
|
275
|
+
'"json": the old internal format, removed in 2.2.0 (exits with an error)',
|
|
212
276
|
)
|
|
213
277
|
|
|
214
278
|
parser.add_argument(
|
|
@@ -222,7 +286,8 @@ class DeepSecretsCliTool:
|
|
|
222
286
|
'--report-diagnostics',
|
|
223
287
|
action='store_true',
|
|
224
288
|
help='Add scan diagnostics to a SARIF report: every file found under the target directory in\n'
|
|
225
|
-
'run.artifacts (scan time in ms, status, skip reason) and per-file errors in
|
|
289
|
+
'run.artifacts (scan time in ms, status, skip reason, analysis depth) and per-file errors in\n'
|
|
290
|
+
'run.invocations.\n',
|
|
226
291
|
)
|
|
227
292
|
|
|
228
293
|
parser.add_argument('--benchmarking-mode', help=argparse.SUPPRESS, action='store_true')
|
|
@@ -231,17 +296,16 @@ class DeepSecretsCliTool:
|
|
|
231
296
|
self.argparser = parser
|
|
232
297
|
|
|
233
298
|
def parse_arguments(self) -> None:
|
|
299
|
+
# a fresh config for every parse: nothing an earlier run set carries over (KI-CLI-12)
|
|
300
|
+
config = self.config = Config()
|
|
234
301
|
|
|
235
302
|
user_args = self.argparser.parse_args(args=self.args[1:])
|
|
236
|
-
if user_args.verbose:
|
|
237
|
-
pass
|
|
238
|
-
# config.set_logging_level(logging.DEBUG)
|
|
239
303
|
|
|
240
304
|
if user_args.disable_masking:
|
|
241
305
|
config.set_disable_masking(True)
|
|
242
306
|
|
|
243
|
-
# set on every parse: the config singleton outlives one run
|
|
244
307
|
config.set_report_diagnostics(user_args.report_diagnostics)
|
|
308
|
+
config.set_confidence_level(user_args.confidence_level)
|
|
245
309
|
|
|
246
310
|
if user_args.benchmarking_mode:
|
|
247
311
|
config._set_benchmarking_mode(True)
|
|
@@ -251,10 +315,16 @@ class DeepSecretsCliTool:
|
|
|
251
315
|
config.set_workdir(user_args.target_dir)
|
|
252
316
|
config.set_oneshot_path(user_args.oneshot)
|
|
253
317
|
config.set_max_file_size(user_args.max_file_size)
|
|
318
|
+
config.set_deep_max_size(user_args.deep_max_size)
|
|
254
319
|
config.set_process_count(user_args.process_count)
|
|
255
|
-
config.set_mp_context(user_args.multiprocessing_context)
|
|
320
|
+
config.set_mp_context(user_args.multiprocessing_context or default_start_method())
|
|
321
|
+
config.set_ci_mode(is_ci_environment() if user_args.ci is None else user_args.ci)
|
|
256
322
|
config.set_verbose(user_args.verbose)
|
|
257
323
|
config.output = Output(type=user_args.outformat, path=user_args.outfile)
|
|
324
|
+
# checked now, not when the report is written after the whole scan
|
|
325
|
+
report_dir = os.path.dirname(get_abspath(user_args.outfile))
|
|
326
|
+
if not os.path.isdir(report_dir):
|
|
327
|
+
raise FileNotFoundException(f'--outfile: the directory {report_dir} does not exist')
|
|
258
328
|
|
|
259
329
|
if user_args.reflect_findings_in_return_code:
|
|
260
330
|
config.return_code_if_findings = True
|
|
@@ -262,16 +332,24 @@ class DeepSecretsCliTool:
|
|
|
262
332
|
EXCLUDE_PATHS_BUILTIN = get_path_inside_package('rules/excluded_paths.json')
|
|
263
333
|
if user_args.excluded_paths is not None:
|
|
264
334
|
rules = [rule.replace('built-in', EXCLUDE_PATHS_BUILTIN) for rule in user_args.excluded_paths]
|
|
335
|
+
if user_args.skip_bundles and 'disable' not in user_args.excluded_paths:
|
|
336
|
+
rules.append(get_path_inside_package('rules/excluded_bundles.json'))
|
|
265
337
|
config.set_global_exclusion_paths(rules)
|
|
266
338
|
|
|
267
|
-
config.engines = []
|
|
268
|
-
|
|
269
339
|
REGEX_BUILTIN_RULESET = get_path_inside_package('rules/regexes.json')
|
|
270
340
|
if user_args.regex_rules is not None:
|
|
271
341
|
rules = [rule.replace('built-in', REGEX_BUILTIN_RULESET) for rule in user_args.regex_rules]
|
|
272
342
|
config.engines.append(RegexEngine)
|
|
273
343
|
config.add_ruleset(RegexRulesetBuilder, rules)
|
|
274
344
|
|
|
345
|
+
CANDIDATE_BUILTIN_RULESET = get_path_inside_package('rules/regex_candidate_scoring_rules.json')
|
|
346
|
+
if user_args.regex_candidate_scoring_rules is not None:
|
|
347
|
+
rules = [
|
|
348
|
+
rule.replace('built-in', CANDIDATE_BUILTIN_RULESET)
|
|
349
|
+
for rule in user_args.regex_candidate_scoring_rules
|
|
350
|
+
]
|
|
351
|
+
config.add_ruleset(RegexCandidateScoringRulesetBuilder, rules)
|
|
352
|
+
|
|
275
353
|
conf_semantic_analysis = user_args.semantic_analysis
|
|
276
354
|
if conf_semantic_analysis is not None and conf_semantic_analysis != DISABLED:
|
|
277
355
|
config.engines.append(SemanticEngine)
|
|
@@ -284,7 +362,9 @@ class DeepSecretsCliTool:
|
|
|
284
362
|
config.add_ruleset(VariableScoringRulesetBuilder, rules)
|
|
285
363
|
|
|
286
364
|
conf_hashed_ruleset = user_args.hashed_values
|
|
287
|
-
|
|
365
|
+
# the flag without a value disables the check, as its help says; an empty ruleset would only keep the lexer on
|
|
366
|
+
# every file for nothing (the hashed engine lexes files over --deep-max-size too)
|
|
367
|
+
if conf_hashed_ruleset and conf_hashed_ruleset != DISABLED:
|
|
288
368
|
config.engines.append(HashedSecretEngine)
|
|
289
369
|
config.add_ruleset(HashedSecretsRulesetBuilder, conf_hashed_ruleset)
|
|
290
370
|
|
|
@@ -293,10 +373,7 @@ class DeepSecretsCliTool:
|
|
|
293
373
|
config.add_ruleset(FalseFindingsBuilder, conf_false_findings_ruleset)
|
|
294
374
|
|
|
295
375
|
def get_current_config(self) -> Config:
|
|
296
|
-
return config
|
|
297
|
-
|
|
298
|
-
def _add_ignorefiles(self, files: List[str]):
|
|
299
|
-
config.set_global_exclusion_paths(files)
|
|
376
|
+
return self.config
|
|
300
377
|
|
|
301
378
|
def start(self) -> int: # pragma: nocover
|
|
302
379
|
startup_time = datetime.now()
|
|
@@ -305,52 +382,78 @@ class DeepSecretsCliTool:
|
|
|
305
382
|
except Exception as e:
|
|
306
383
|
logger.exception(e)
|
|
307
384
|
return ReturnCodes.ERROR
|
|
385
|
+
config = self.config
|
|
308
386
|
|
|
309
387
|
if config.output.type == 'json':
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
388
|
+
self._refuse_json_output()
|
|
389
|
+
return ReturnCodes.ERROR
|
|
390
|
+
|
|
391
|
+
self._print_plan()
|
|
392
|
+
|
|
393
|
+
try:
|
|
394
|
+
mode = CliScanMode(config=config)
|
|
395
|
+
except TempDirTooLongError as e:
|
|
396
|
+
console.print(f'[bold red][!] Cannot start worker processes: {e}.[/bold red]')
|
|
397
|
+
return ReturnCodes.ERROR
|
|
398
|
+
|
|
399
|
+
console.line()
|
|
400
|
+
|
|
401
|
+
if not config.ci_mode:
|
|
402
|
+
# run() starts it once the worker pool exists
|
|
403
|
+
mode.set_progress_bar(progress_bar)
|
|
404
|
+
mode.progress_bar.set_start_time(startup_time)
|
|
405
|
+
|
|
406
|
+
try:
|
|
407
|
+
findings, errors, timings = mode.run()
|
|
408
|
+
except WorkerStartupError as e:
|
|
409
|
+
if mode.progress_bar is not None:
|
|
410
|
+
mode.progress_bar.stop()
|
|
411
|
+
console.print(f'[bold red][!] Scan stopped: {e}. Every file it took would fail the same way.[/bold red]')
|
|
412
|
+
self._release(mode)
|
|
413
|
+
return ReturnCodes.ERROR
|
|
414
|
+
|
|
415
|
+
if mode.progress_bar is not None:
|
|
416
|
+
mode.progress_bar.stop()
|
|
417
|
+
report_path = get_abspath(config.output.path)
|
|
418
|
+
self._print_summary(mode, findings, errors, datetime.now() - startup_time, report_path)
|
|
419
|
+
|
|
420
|
+
if config._benchmarking_mode is True:
|
|
421
|
+
return findings, errors, timings, mode._oneshot_file
|
|
422
|
+
|
|
423
|
+
self._write_report(mode, findings, report_path)
|
|
424
|
+
self._say_goodbye(findings)
|
|
425
|
+
self._release(mode)
|
|
426
|
+
|
|
427
|
+
if len(findings) > 0 and config.return_code_if_findings:
|
|
428
|
+
return ReturnCodes.FINDINGS_DETECTED
|
|
429
|
+
|
|
430
|
+
return ReturnCodes.OK
|
|
431
|
+
|
|
432
|
+
@staticmethod
|
|
433
|
+
def _refuse_json_output() -> None: # pragma: nocover
|
|
434
|
+
console.print('\n')
|
|
435
|
+
console.print(
|
|
436
|
+
Align(
|
|
437
|
+
Panel(
|
|
438
|
+
'The internal JSON report format was deprecated in 2.1 and is removed in 2.2.0.\n'
|
|
439
|
+
'Use --outformat sarif (the default) or dojo-sarif.',
|
|
440
|
+
padding=(1, 2),
|
|
441
|
+
title=Text('SARIF IS NOW DEFAULT OUTPUT FORMAT', style='reverse'),
|
|
442
|
+
highlight=True,
|
|
443
|
+
subtitle=Text(' --outformat sarif ', style='reverse'),
|
|
444
|
+
title_align='center',
|
|
445
|
+
width=90,
|
|
446
|
+
subtitle_align='center',
|
|
447
|
+
box=box.HEAVY,
|
|
448
|
+
style='black on orange_red1',
|
|
449
|
+
expand=False,
|
|
450
|
+
),
|
|
451
|
+
align='center',
|
|
452
|
+
)
|
|
453
|
+
)
|
|
353
454
|
|
|
455
|
+
def _print_plan(self) -> None: # pragma: nocover
|
|
456
|
+
config = self.config
|
|
354
457
|
console.rule(
|
|
355
458
|
f'Planning a scan against {config.workdir_path} using {config.process_count} process(es)', characters='='
|
|
356
459
|
)
|
|
@@ -371,31 +474,9 @@ class DeepSecretsCliTool:
|
|
|
371
474
|
f'[bold yellow]:warning:[/bold yellow] The tool will return code of {ReturnCodes.FINDINGS_DETECTED} if any findings are detected\n'
|
|
372
475
|
)
|
|
373
476
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
mode.set_progress_bar(progress_bar)
|
|
379
|
-
mode.progress_bar.set_start_time(startup_time)
|
|
380
|
-
mode.progress_bar.start()
|
|
381
|
-
|
|
382
|
-
findings: List[Finding]
|
|
383
|
-
errors: Dict[str, List[str]]
|
|
384
|
-
timings: Dict[str, int]
|
|
385
|
-
|
|
386
|
-
findings, errors, timings = mode.run()
|
|
387
|
-
|
|
388
|
-
'''
|
|
389
|
-
for finding in findings:
|
|
390
|
-
if finding._mapped_on_file is False:
|
|
391
|
-
continue
|
|
392
|
-
finding.file = None
|
|
393
|
-
'''
|
|
394
|
-
|
|
395
|
-
mode.progress_bar.stop()
|
|
396
|
-
finish_time = datetime.now()
|
|
397
|
-
report_path = get_abspath(config.output.path)
|
|
398
|
-
|
|
477
|
+
def _print_summary(
|
|
478
|
+
self, mode: CliScanMode, findings: List[Finding], errors: Dict[str, List[str]], elapsed, report_path: str
|
|
479
|
+
) -> None: # pragma: nocover
|
|
399
480
|
console.line()
|
|
400
481
|
console.print('[bold green]Scanning finished successfully', justify='center')
|
|
401
482
|
console.line()
|
|
@@ -416,7 +497,7 @@ class DeepSecretsCliTool:
|
|
|
416
497
|
Align('Processed Files (Tokens)', vertical='middle'),
|
|
417
498
|
f'{str(len(mode.filepaths))} ({mode.stats.tokens_processed})',
|
|
418
499
|
)
|
|
419
|
-
table.add_row(Align('Elapsed', vertical='middle'), f'{
|
|
500
|
+
table.add_row(Align('Elapsed', vertical='middle'), f'{elapsed.total_seconds():.1f}s')
|
|
420
501
|
errors_line_color = '[bold red]' if len(errors.keys()) > 0 else '[bold green]'
|
|
421
502
|
table.add_row(
|
|
422
503
|
Align(f'{errors_line_color}File Errors', vertical='middle'),
|
|
@@ -429,36 +510,24 @@ class DeepSecretsCliTool:
|
|
|
429
510
|
Align(f'{findings_line_color}Potential Findings', vertical='middle'),
|
|
430
511
|
f'{findings_line_color}{str(len(findings))}',
|
|
431
512
|
)
|
|
432
|
-
table.add_row(Align(f'Report Location ({config.output.type})', vertical='middle'), report_path)
|
|
513
|
+
table.add_row(Align(f'Report Location ({self.config.output.type})', vertical='middle'), report_path)
|
|
433
514
|
console.print(Align(table, align='center'))
|
|
434
515
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
516
|
+
def _write_report(self, mode: CliScanMode, findings: List[Finding], report_path: str) -> None:
|
|
517
|
+
# sarif and dojo-sarif are the same document; built before the file is opened, so a failure leaves no
|
|
518
|
+
# truncated report behind
|
|
519
|
+
report = (
|
|
520
|
+
DojoSarifResponseBuilder()
|
|
521
|
+
.with_current_mode(mode)
|
|
522
|
+
.with_findings_list(findings)
|
|
523
|
+
.with_masking_enabled(not self.config.disable_masking)
|
|
524
|
+
.build()
|
|
525
|
+
)
|
|
438
526
|
with open(report_path, 'w+') as f:
|
|
527
|
+
f.write(to_json(report))
|
|
439
528
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
BuiltinFormatResponseBuilder()
|
|
443
|
-
.with_current_mode(mode)
|
|
444
|
-
.with_findings_list(findings)
|
|
445
|
-
.with_masking_enabled(not config.disable_masking)
|
|
446
|
-
.build(),
|
|
447
|
-
f,
|
|
448
|
-
)
|
|
449
|
-
|
|
450
|
-
if config.output.type in ['sarif', 'dojo-sarif']:
|
|
451
|
-
f.write(
|
|
452
|
-
to_json(
|
|
453
|
-
DojoSarifResponseBuilder()
|
|
454
|
-
.with_current_mode(mode)
|
|
455
|
-
.with_findings_list(findings)
|
|
456
|
-
.with_masking_enabled(not config.disable_masking)
|
|
457
|
-
.build()
|
|
458
|
-
)
|
|
459
|
-
)
|
|
460
|
-
|
|
461
|
-
if len(findings) > 0 and config.disable_masking:
|
|
529
|
+
def _say_goodbye(self, findings: List[Finding]) -> None: # pragma: nocover
|
|
530
|
+
if len(findings) > 0 and self.config.disable_masking:
|
|
462
531
|
console.print(
|
|
463
532
|
'[bold red]:warning: SECRETS MASKING WAS DISABLED, THE REPORT CONTAINS POTENTIAL SECRETS IN PLAINTEXT.\nBE CAREFUL!',
|
|
464
533
|
justify='center',
|
|
@@ -473,9 +542,10 @@ class DeepSecretsCliTool:
|
|
|
473
542
|
console.line()
|
|
474
543
|
console.print(Align('[bold green]FINISHED', align='center'))
|
|
475
544
|
console.line(2)
|
|
476
|
-
mode.dispose()
|
|
477
545
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
546
|
+
@staticmethod
|
|
547
|
+
def _release(mode: CliScanMode) -> None:
|
|
548
|
+
mode.dispose()
|
|
549
|
+
# the pool and the manager are gone: wait for the forkserver, so whoever waits for this process sees the
|
|
550
|
+
# workers' CPU time (KI-CLI-42)
|
|
551
|
+
stop_forkserver()
|