layer8-secretscan 0.1.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.
Files changed (40) hide show
  1. layer8_secretscan-0.1.0/PKG-INFO +91 -0
  2. layer8_secretscan-0.1.0/README.md +76 -0
  3. layer8_secretscan-0.1.0/pyproject.toml +25 -0
  4. layer8_secretscan-0.1.0/setup.cfg +4 -0
  5. layer8_secretscan-0.1.0/src/layer8_secretscan.egg-info/PKG-INFO +91 -0
  6. layer8_secretscan-0.1.0/src/layer8_secretscan.egg-info/SOURCES.txt +38 -0
  7. layer8_secretscan-0.1.0/src/layer8_secretscan.egg-info/dependency_links.txt +1 -0
  8. layer8_secretscan-0.1.0/src/layer8_secretscan.egg-info/entry_points.txt +2 -0
  9. layer8_secretscan-0.1.0/src/layer8_secretscan.egg-info/requires.txt +9 -0
  10. layer8_secretscan-0.1.0/src/layer8_secretscan.egg-info/top_level.txt +1 -0
  11. layer8_secretscan-0.1.0/src/secretscan/__init__.py +1 -0
  12. layer8_secretscan-0.1.0/src/secretscan/__main__.py +3 -0
  13. layer8_secretscan-0.1.0/src/secretscan/cli.py +243 -0
  14. layer8_secretscan-0.1.0/src/secretscan/config.py +187 -0
  15. layer8_secretscan-0.1.0/src/secretscan/model.py +409 -0
  16. layer8_secretscan-0.1.0/src/secretscan/redact.py +43 -0
  17. layer8_secretscan-0.1.0/src/secretscan/report/__init__.py +6 -0
  18. layer8_secretscan-0.1.0/src/secretscan/report/csv_report.py +26 -0
  19. layer8_secretscan-0.1.0/src/secretscan/report/html_report.py +235 -0
  20. layer8_secretscan-0.1.0/src/secretscan/report/json_report.py +15 -0
  21. layer8_secretscan-0.1.0/src/secretscan/report/table_report.py +145 -0
  22. layer8_secretscan-0.1.0/src/secretscan/rules/__init__.py +14 -0
  23. layer8_secretscan-0.1.0/src/secretscan/rules/default.yaml +132 -0
  24. layer8_secretscan-0.1.0/src/secretscan/rules/entropy.py +17 -0
  25. layer8_secretscan-0.1.0/src/secretscan/rules/loader.py +34 -0
  26. layer8_secretscan-0.1.0/src/secretscan/rules/placeholder.py +66 -0
  27. layer8_secretscan-0.1.0/src/secretscan/rules/rule.py +70 -0
  28. layer8_secretscan-0.1.0/src/secretscan/scanner/__init__.py +3 -0
  29. layer8_secretscan-0.1.0/src/secretscan/scanner/chrome.py +518 -0
  30. layer8_secretscan-0.1.0/src/secretscan/scanner/content.py +104 -0
  31. layer8_secretscan-0.1.0/src/secretscan/scanner/docker.py +81 -0
  32. layer8_secretscan-0.1.0/src/secretscan/scanner/engine.py +186 -0
  33. layer8_secretscan-0.1.0/src/secretscan/scanner/filesystem.py +240 -0
  34. layer8_secretscan-0.1.0/src/secretscan/scanner/internet.py +48 -0
  35. layer8_secretscan-0.1.0/src/secretscan/scanner/keychain.py +116 -0
  36. layer8_secretscan-0.1.0/src/secretscan/scanner/lan.py +167 -0
  37. layer8_secretscan-0.1.0/src/secretscan/scanner/network.py +156 -0
  38. layer8_secretscan-0.1.0/src/secretscan/scanner/process_env.py +132 -0
  39. layer8_secretscan-0.1.0/src/secretscan/scanner/shell_history.py +70 -0
  40. layer8_secretscan-0.1.0/src/secretscan/scanner/tools.py +174 -0
@@ -0,0 +1,91 @@
1
+ Metadata-Version: 2.4
2
+ Name: layer8-secretscan
3
+ Version: 0.1.0
4
+ Summary: Scan your machine for accidentally exposed secrets — API keys, tokens, passwords, private keys — across filesystem, shell history, keychain, processes, and more.
5
+ License-Expression: MIT
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: pyyaml>=6.0
9
+ Provides-Extra: chrome
10
+ Requires-Dist: websocket-client>=1.6.0; extra == "chrome"
11
+ Requires-Dist: cryptography>=41.0; extra == "chrome"
12
+ Provides-Extra: all
13
+ Requires-Dist: websocket-client>=1.6.0; extra == "all"
14
+ Requires-Dist: cryptography>=41.0; extra == "all"
15
+
16
+ # secretscan
17
+
18
+ A personal security-hygiene tool: scans your own machine for accidentally
19
+ exposed secrets (API keys, tokens, passwords, private keys), audits what's
20
+ installed and what's exposed (Docker containers, listening ports), and can
21
+ optionally check your local network and internet connectivity.
22
+
23
+ ## Install
24
+
25
+ ```sh
26
+ pip install .
27
+ ```
28
+
29
+ Or for development:
30
+
31
+ ```sh
32
+ pip install -e .
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ ```sh
38
+ # default scan: everything except lan/internet, print a table
39
+ secretscan run
40
+
41
+ # scan a specific directory, filesystem module only
42
+ secretscan run --paths ~/Projects --modules filesystem
43
+
44
+ # full scan including LAN and internet, all formats
45
+ secretscan run --modules filesystem,shell-history,keychain,process-env,installed-tools,network,docker,lan,internet \
46
+ --yes-lan --format table,json,html --output-dir ./report
47
+
48
+ # list all detection rules
49
+ secretscan rules list
50
+
51
+ # use a custom rules file
52
+ secretscan run --rules-file my-rules.yaml
53
+ ```
54
+
55
+ ## Modules
56
+
57
+ | Module | Default | What it does |
58
+ |---|---|---|
59
+ | `filesystem` | yes | Scans files under `--paths` (default `$HOME`) for secret patterns |
60
+ | `shell-history` | yes | Scans `.zsh_history`, `.bash_history`, and other history files |
61
+ | `keychain` | yes | Audits macOS Keychain metadata (never decrypts values) |
62
+ | `process-env` | yes | Inspects your own running processes' command lines |
63
+ | `installed-tools` | yes | Inventories security/dev tools and packages |
64
+ | `network` | yes | Lists TCP/UDP ports this machine is listening on |
65
+ | `docker` | yes | Lists running containers, checks for exposed Docker API |
66
+ | `lan` | opt-in | Probes other devices on your local network (`--yes-lan`) |
67
+ | `internet` | opt-in | Tests outbound connectivity to public endpoints |
68
+
69
+ ## Output formats
70
+
71
+ - `table` — colored terminal output (default)
72
+ - `json` — machine-readable `{meta, findings}` document
73
+ - `html` — self-contained HTML report with dark/light theme
74
+ - `csv` — flat CSV of findings
75
+
76
+ ## Detection
77
+
78
+ 14 built-in rules covering AWS, GitHub, GitLab, Slack, Stripe, Google, npm,
79
+ PEM private keys, and generic password/secret/token assignments. Each rule
80
+ uses regex matching with optional Shannon entropy scoring and keyword
81
+ pre-filtering for performance.
82
+
83
+ Values that look like placeholders (`{{password}}`, `changeme`, `your_api_key_here`)
84
+ are detected and downgraded rather than competing with real findings.
85
+
86
+ ## What this tool does NOT do
87
+
88
+ - No git-history scanning (use Gitleaks/TruffleHog for that)
89
+ - No live credential verification
90
+ - No bypassing macOS security prompts
91
+ - Secret detection modules never make outbound network calls
@@ -0,0 +1,76 @@
1
+ # secretscan
2
+
3
+ A personal security-hygiene tool: scans your own machine for accidentally
4
+ exposed secrets (API keys, tokens, passwords, private keys), audits what's
5
+ installed and what's exposed (Docker containers, listening ports), and can
6
+ optionally check your local network and internet connectivity.
7
+
8
+ ## Install
9
+
10
+ ```sh
11
+ pip install .
12
+ ```
13
+
14
+ Or for development:
15
+
16
+ ```sh
17
+ pip install -e .
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ```sh
23
+ # default scan: everything except lan/internet, print a table
24
+ secretscan run
25
+
26
+ # scan a specific directory, filesystem module only
27
+ secretscan run --paths ~/Projects --modules filesystem
28
+
29
+ # full scan including LAN and internet, all formats
30
+ secretscan run --modules filesystem,shell-history,keychain,process-env,installed-tools,network,docker,lan,internet \
31
+ --yes-lan --format table,json,html --output-dir ./report
32
+
33
+ # list all detection rules
34
+ secretscan rules list
35
+
36
+ # use a custom rules file
37
+ secretscan run --rules-file my-rules.yaml
38
+ ```
39
+
40
+ ## Modules
41
+
42
+ | Module | Default | What it does |
43
+ |---|---|---|
44
+ | `filesystem` | yes | Scans files under `--paths` (default `$HOME`) for secret patterns |
45
+ | `shell-history` | yes | Scans `.zsh_history`, `.bash_history`, and other history files |
46
+ | `keychain` | yes | Audits macOS Keychain metadata (never decrypts values) |
47
+ | `process-env` | yes | Inspects your own running processes' command lines |
48
+ | `installed-tools` | yes | Inventories security/dev tools and packages |
49
+ | `network` | yes | Lists TCP/UDP ports this machine is listening on |
50
+ | `docker` | yes | Lists running containers, checks for exposed Docker API |
51
+ | `lan` | opt-in | Probes other devices on your local network (`--yes-lan`) |
52
+ | `internet` | opt-in | Tests outbound connectivity to public endpoints |
53
+
54
+ ## Output formats
55
+
56
+ - `table` — colored terminal output (default)
57
+ - `json` — machine-readable `{meta, findings}` document
58
+ - `html` — self-contained HTML report with dark/light theme
59
+ - `csv` — flat CSV of findings
60
+
61
+ ## Detection
62
+
63
+ 14 built-in rules covering AWS, GitHub, GitLab, Slack, Stripe, Google, npm,
64
+ PEM private keys, and generic password/secret/token assignments. Each rule
65
+ uses regex matching with optional Shannon entropy scoring and keyword
66
+ pre-filtering for performance.
67
+
68
+ Values that look like placeholders (`{{password}}`, `changeme`, `your_api_key_here`)
69
+ are detected and downgraded rather than competing with real findings.
70
+
71
+ ## What this tool does NOT do
72
+
73
+ - No git-history scanning (use Gitleaks/TruffleHog for that)
74
+ - No live credential verification
75
+ - No bypassing macOS security prompts
76
+ - Secret detection modules never make outbound network calls
@@ -0,0 +1,25 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "layer8-secretscan"
7
+ version = "0.1.0"
8
+ description = "Scan your machine for accidentally exposed secrets — API keys, tokens, passwords, private keys — across filesystem, shell history, keychain, processes, and more."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ dependencies = ["pyyaml>=6.0"]
13
+
14
+ [project.optional-dependencies]
15
+ chrome = ["websocket-client>=1.6.0", "cryptography>=41.0"]
16
+ all = ["websocket-client>=1.6.0", "cryptography>=41.0"]
17
+
18
+ [project.scripts]
19
+ secretscan = "secretscan.cli:main"
20
+
21
+ [tool.setuptools.packages.find]
22
+ where = ["src"]
23
+
24
+ [tool.setuptools.package-data]
25
+ secretscan = ["rules/*.yaml"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,91 @@
1
+ Metadata-Version: 2.4
2
+ Name: layer8-secretscan
3
+ Version: 0.1.0
4
+ Summary: Scan your machine for accidentally exposed secrets — API keys, tokens, passwords, private keys — across filesystem, shell history, keychain, processes, and more.
5
+ License-Expression: MIT
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: pyyaml>=6.0
9
+ Provides-Extra: chrome
10
+ Requires-Dist: websocket-client>=1.6.0; extra == "chrome"
11
+ Requires-Dist: cryptography>=41.0; extra == "chrome"
12
+ Provides-Extra: all
13
+ Requires-Dist: websocket-client>=1.6.0; extra == "all"
14
+ Requires-Dist: cryptography>=41.0; extra == "all"
15
+
16
+ # secretscan
17
+
18
+ A personal security-hygiene tool: scans your own machine for accidentally
19
+ exposed secrets (API keys, tokens, passwords, private keys), audits what's
20
+ installed and what's exposed (Docker containers, listening ports), and can
21
+ optionally check your local network and internet connectivity.
22
+
23
+ ## Install
24
+
25
+ ```sh
26
+ pip install .
27
+ ```
28
+
29
+ Or for development:
30
+
31
+ ```sh
32
+ pip install -e .
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ ```sh
38
+ # default scan: everything except lan/internet, print a table
39
+ secretscan run
40
+
41
+ # scan a specific directory, filesystem module only
42
+ secretscan run --paths ~/Projects --modules filesystem
43
+
44
+ # full scan including LAN and internet, all formats
45
+ secretscan run --modules filesystem,shell-history,keychain,process-env,installed-tools,network,docker,lan,internet \
46
+ --yes-lan --format table,json,html --output-dir ./report
47
+
48
+ # list all detection rules
49
+ secretscan rules list
50
+
51
+ # use a custom rules file
52
+ secretscan run --rules-file my-rules.yaml
53
+ ```
54
+
55
+ ## Modules
56
+
57
+ | Module | Default | What it does |
58
+ |---|---|---|
59
+ | `filesystem` | yes | Scans files under `--paths` (default `$HOME`) for secret patterns |
60
+ | `shell-history` | yes | Scans `.zsh_history`, `.bash_history`, and other history files |
61
+ | `keychain` | yes | Audits macOS Keychain metadata (never decrypts values) |
62
+ | `process-env` | yes | Inspects your own running processes' command lines |
63
+ | `installed-tools` | yes | Inventories security/dev tools and packages |
64
+ | `network` | yes | Lists TCP/UDP ports this machine is listening on |
65
+ | `docker` | yes | Lists running containers, checks for exposed Docker API |
66
+ | `lan` | opt-in | Probes other devices on your local network (`--yes-lan`) |
67
+ | `internet` | opt-in | Tests outbound connectivity to public endpoints |
68
+
69
+ ## Output formats
70
+
71
+ - `table` — colored terminal output (default)
72
+ - `json` — machine-readable `{meta, findings}` document
73
+ - `html` — self-contained HTML report with dark/light theme
74
+ - `csv` — flat CSV of findings
75
+
76
+ ## Detection
77
+
78
+ 14 built-in rules covering AWS, GitHub, GitLab, Slack, Stripe, Google, npm,
79
+ PEM private keys, and generic password/secret/token assignments. Each rule
80
+ uses regex matching with optional Shannon entropy scoring and keyword
81
+ pre-filtering for performance.
82
+
83
+ Values that look like placeholders (`{{password}}`, `changeme`, `your_api_key_here`)
84
+ are detected and downgraded rather than competing with real findings.
85
+
86
+ ## What this tool does NOT do
87
+
88
+ - No git-history scanning (use Gitleaks/TruffleHog for that)
89
+ - No live credential verification
90
+ - No bypassing macOS security prompts
91
+ - Secret detection modules never make outbound network calls
@@ -0,0 +1,38 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/layer8_secretscan.egg-info/PKG-INFO
4
+ src/layer8_secretscan.egg-info/SOURCES.txt
5
+ src/layer8_secretscan.egg-info/dependency_links.txt
6
+ src/layer8_secretscan.egg-info/entry_points.txt
7
+ src/layer8_secretscan.egg-info/requires.txt
8
+ src/layer8_secretscan.egg-info/top_level.txt
9
+ src/secretscan/__init__.py
10
+ src/secretscan/__main__.py
11
+ src/secretscan/cli.py
12
+ src/secretscan/config.py
13
+ src/secretscan/model.py
14
+ src/secretscan/redact.py
15
+ src/secretscan/report/__init__.py
16
+ src/secretscan/report/csv_report.py
17
+ src/secretscan/report/html_report.py
18
+ src/secretscan/report/json_report.py
19
+ src/secretscan/report/table_report.py
20
+ src/secretscan/rules/__init__.py
21
+ src/secretscan/rules/default.yaml
22
+ src/secretscan/rules/entropy.py
23
+ src/secretscan/rules/loader.py
24
+ src/secretscan/rules/placeholder.py
25
+ src/secretscan/rules/rule.py
26
+ src/secretscan/scanner/__init__.py
27
+ src/secretscan/scanner/chrome.py
28
+ src/secretscan/scanner/content.py
29
+ src/secretscan/scanner/docker.py
30
+ src/secretscan/scanner/engine.py
31
+ src/secretscan/scanner/filesystem.py
32
+ src/secretscan/scanner/internet.py
33
+ src/secretscan/scanner/keychain.py
34
+ src/secretscan/scanner/lan.py
35
+ src/secretscan/scanner/network.py
36
+ src/secretscan/scanner/process_env.py
37
+ src/secretscan/scanner/shell_history.py
38
+ src/secretscan/scanner/tools.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ secretscan = secretscan.cli:main
@@ -0,0 +1,9 @@
1
+ pyyaml>=6.0
2
+
3
+ [all]
4
+ websocket-client>=1.6.0
5
+ cryptography>=41.0
6
+
7
+ [chrome]
8
+ websocket-client>=1.6.0
9
+ cryptography>=41.0
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,3 @@
1
+ from secretscan.cli import main
2
+
3
+ main()
@@ -0,0 +1,243 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import os
5
+ import sys
6
+ from datetime import datetime
7
+ from pathlib import Path
8
+
9
+ from secretscan import __version__
10
+ from secretscan.config import Config
11
+ from secretscan.model import Confidence, Module, Severity, DEFAULT_MODULES, OPT_IN_MODULES
12
+
13
+
14
+ MODULE_NAMES = {
15
+ "filesystem": Module.FILESYSTEM,
16
+ "shell-history": Module.SHELL_HISTORY,
17
+ "keychain": Module.KEYCHAIN,
18
+ "process-env": Module.PROCESS_ENV,
19
+ "installed-tools": Module.INSTALLED_TOOLS,
20
+ "network": Module.NETWORK,
21
+ "docker": Module.DOCKER,
22
+ "chrome": Module.CHROME,
23
+ "lan": Module.LAN,
24
+ "internet": Module.INTERNET,
25
+ }
26
+
27
+ BAR_WIDTH = 30
28
+
29
+
30
+ def _progress_bar(done: int, total: int, module_name: str = "") -> None:
31
+ if total == 0:
32
+ return
33
+ pct = int(done / total * 100)
34
+ filled = int(BAR_WIDTH * done / total)
35
+ bar = "█" * filled + "░" * (BAR_WIDTH - filled)
36
+ label = f" {module_name}" if module_name else ""
37
+ sys.stderr.write(f"\r [{bar}] {pct:3d}% ({done}/{total}){label} ")
38
+ sys.stderr.flush()
39
+ if done == total:
40
+ sys.stderr.write("\n")
41
+
42
+
43
+ def _render_outputs(cfg: Config, result) -> None:
44
+ for fmt in cfg.formats:
45
+ if fmt == "json":
46
+ from secretscan.report.json_report import render_json
47
+ if cfg.output_dir:
48
+ os.makedirs(cfg.output_dir, exist_ok=True)
49
+ with open(os.path.join(cfg.output_dir, "report.json"), "w") as f:
50
+ render_json(f, result.findings, result.meta)
51
+ else:
52
+ render_json(sys.stdout, result.findings, result.meta)
53
+ elif fmt == "html":
54
+ from secretscan.report.html_report import render_html
55
+ if cfg.output_dir:
56
+ os.makedirs(cfg.output_dir, exist_ok=True)
57
+ with open(os.path.join(cfg.output_dir, "report.html"), "w") as f:
58
+ render_html(f, result.findings, result.meta)
59
+ sys.stderr.write(f" HTML report written to {cfg.output_dir}/report.html\n")
60
+ else:
61
+ render_html(sys.stdout, result.findings, result.meta)
62
+ elif fmt == "csv":
63
+ from secretscan.report.csv_report import render_csv
64
+ if cfg.output_dir:
65
+ os.makedirs(cfg.output_dir, exist_ok=True)
66
+ with open(os.path.join(cfg.output_dir, "report.csv"), "w") as f:
67
+ render_csv(f, result.findings, result.meta)
68
+ else:
69
+ render_csv(sys.stdout, result.findings, result.meta)
70
+ elif fmt == "table":
71
+ from secretscan.report.table_report import render_table
72
+ render_table(sys.stderr, result.findings, result.meta, no_color=False)
73
+
74
+
75
+ def _run(args: argparse.Namespace) -> int:
76
+ from secretscan.scanner.engine import run_scan
77
+
78
+ modules: list[Module] = []
79
+ if args.modules:
80
+ for name in args.modules.split(","):
81
+ name = name.strip()
82
+ try:
83
+ modules.append(Module(name))
84
+ except ValueError:
85
+ sys.stderr.write(f"unknown module: {name}\n")
86
+ return 2
87
+ else:
88
+ modules = list(DEFAULT_MODULES)
89
+
90
+ if args.yes_lan and Module.LAN not in modules:
91
+ modules.append(Module.LAN)
92
+
93
+ if Module.LAN in modules and not args.yes_lan:
94
+ sys.stderr.write(
95
+ " The 'lan' module actively probes other devices on your network.\n"
96
+ " Pass --yes-lan to confirm you're authorized to scan that network.\n"
97
+ )
98
+ modules = [m for m in modules if m != Module.LAN]
99
+
100
+ if Module.INTERNET in modules:
101
+ sys.stderr.write(" The 'internet' module makes real outbound calls to public endpoints.\n")
102
+
103
+ paths = args.paths.split(",") if args.paths else [str(Path.home())]
104
+
105
+ cfg = Config(
106
+ paths=paths,
107
+ modules=modules,
108
+ exclude_globs=args.exclude.split(",") if args.exclude else [],
109
+ no_default_excludes=args.no_default_excludes,
110
+ include_volumes=args.include_volumes,
111
+ min_severity=Severity(args.min_severity),
112
+ min_confidence=Confidence(args.min_confidence),
113
+ rules_file=args.rules_file or "",
114
+ concurrency=args.concurrency,
115
+ reveal_length=args.reveal_length,
116
+ formats=args.format.split(",") if args.format else ["table"],
117
+ output_dir=args.output_dir or "",
118
+ )
119
+
120
+ module_order = list(cfg.modules)
121
+
122
+ def progress(done: int, total: int) -> None:
123
+ mod_name = module_order[done - 1].value if done <= len(module_order) else ""
124
+ _progress_bar(done, total, mod_name)
125
+
126
+ result = run_scan(cfg, progress=progress)
127
+ _render_outputs(cfg, result)
128
+ return 1 if result.findings else 0
129
+
130
+
131
+ def _fullscan(args: argparse.Namespace) -> int:
132
+ from secretscan.scanner.engine import run_scan
133
+
134
+ all_modules = list(Module)
135
+
136
+ output_dir = args.output_dir
137
+ if not output_dir:
138
+ output_dir = str(Path("reports") / datetime.now().strftime("%Y-%m-%d_%H-%M-%S"))
139
+
140
+ paths = args.paths.split(",") if args.paths else [str(Path.home())]
141
+
142
+ cfg = Config(
143
+ paths=paths,
144
+ modules=all_modules,
145
+ rules_file=args.rules_file or "",
146
+ concurrency=args.concurrency,
147
+ reveal_length=args.reveal_length,
148
+ formats=["html", "json", "table"],
149
+ output_dir=output_dir,
150
+ )
151
+
152
+ sys.stderr.write(f"\n secretscan fullscan\n")
153
+ sys.stderr.write(f" Paths: {', '.join(paths)}\n")
154
+ sys.stderr.write(f" Modules: {', '.join(m.value for m in all_modules)}\n")
155
+ sys.stderr.write(f" Output: {output_dir}\n\n")
156
+
157
+ module_order = list(cfg.modules)
158
+
159
+ def progress(done: int, total: int) -> None:
160
+ mod_name = module_order[done - 1].value if done <= len(module_order) else ""
161
+ _progress_bar(done, total, mod_name)
162
+
163
+ result = run_scan(cfg, progress=progress)
164
+ _render_outputs(cfg, result)
165
+
166
+ html_path = os.path.join(output_dir, "report.html")
167
+ if os.path.isfile(html_path):
168
+ sys.stderr.write(f"\n Report: {html_path}\n")
169
+
170
+ return 1 if result.findings else 0
171
+
172
+
173
+ def _rules_list(args: argparse.Namespace) -> int:
174
+ from secretscan.rules.loader import load_default_rules, load_rules_file
175
+
176
+ if args.rules_file:
177
+ rules = load_rules_file(args.rules_file)
178
+ else:
179
+ rules = load_default_rules()
180
+
181
+ fmt = "{:<35s} {:<10s} {:<50s}"
182
+ sys.stdout.write(fmt.format("ID", "SEVERITY", "DESCRIPTION") + "\n")
183
+ sys.stdout.write("─" * 95 + "\n")
184
+ for r in rules:
185
+ sys.stdout.write(
186
+ fmt.format(r.rule.id, r.rule.severity, r.rule.description[:50]) + "\n"
187
+ )
188
+ sys.stdout.write(f"\n{len(rules)} rules loaded.\n")
189
+ return 0
190
+
191
+
192
+ def main(argv: list[str] | None = None) -> None:
193
+ parser = argparse.ArgumentParser(
194
+ prog="secretscan",
195
+ description="Scan your machine for accidentally exposed secrets.",
196
+ )
197
+ parser.add_argument("--version", action="version", version=f"secretscan {__version__}")
198
+ subparsers = parser.add_subparsers(dest="command")
199
+
200
+ # --- run ---
201
+ run_parser = subparsers.add_parser("run", help="Run a scan with selected modules")
202
+ run_parser.add_argument("--paths", default=None, help="Comma-separated roots to scan (default: $HOME)")
203
+ run_parser.add_argument("--modules", default=None, help="Comma-separated modules to run")
204
+ run_parser.add_argument("--exclude", default=None, help="Additional glob patterns to exclude")
205
+ run_parser.add_argument("--no-default-excludes", action="store_true")
206
+ run_parser.add_argument("--include-volumes", action="store_true", help="Include /Volumes mounts")
207
+ run_parser.add_argument("--yes-lan", action="store_true", help="Confirm LAN probing authorization")
208
+ run_parser.add_argument("--min-severity", default="low", choices=["low", "medium", "high", "critical"])
209
+ run_parser.add_argument("--min-confidence", default="low", choices=["low", "medium", "high"])
210
+ run_parser.add_argument("--rules-file", default=None, help="Custom rules YAML file")
211
+ run_parser.add_argument("--format", default="table", help="Output formats: table,json,html,csv")
212
+ run_parser.add_argument("--output-dir", default=None, help="Write file reports to this directory")
213
+ run_parser.add_argument("--reveal-length", action="store_true", help="Show coarse length hint on redacted values")
214
+ run_parser.add_argument("--concurrency", type=int, default=0, help="Worker threads (default: CPU count)")
215
+ run_parser.add_argument("--no-color", action="store_true", help="Disable ANSI colors in table output")
216
+
217
+ # --- fullscan ---
218
+ full_parser = subparsers.add_parser("fullscan", help="Run ALL modules (incl. Chrome, LAN, Internet) and generate HTML+JSON reports")
219
+ full_parser.add_argument("--paths", default=None, help="Comma-separated roots to scan (default: $HOME)")
220
+ full_parser.add_argument("--output-dir", default=None, help="Write reports here (default: reports/<timestamp>)")
221
+ full_parser.add_argument("--rules-file", default=None, help="Custom rules YAML file")
222
+ full_parser.add_argument("--reveal-length", action="store_true")
223
+ full_parser.add_argument("--concurrency", type=int, default=0)
224
+
225
+ # --- rules ---
226
+ rules_parser = subparsers.add_parser("rules", help="Manage detection rules")
227
+ rules_sub = rules_parser.add_subparsers(dest="rules_command")
228
+ list_parser = rules_sub.add_parser("list", help="List all detection rules")
229
+ list_parser.add_argument("--rules-file", default=None)
230
+
231
+ parsed = parser.parse_args(argv)
232
+
233
+ if parsed.command == "run":
234
+ code = _run(parsed)
235
+ elif parsed.command == "fullscan":
236
+ code = _fullscan(parsed)
237
+ elif parsed.command == "rules" and getattr(parsed, "rules_command", None) == "list":
238
+ code = _rules_list(parsed)
239
+ else:
240
+ parser.print_help()
241
+ code = 0
242
+
243
+ sys.exit(code)