@activemind/scd 1.4.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/LICENSE.md +35 -0
- package/README.md +417 -0
- package/bin/scd.js +140 -0
- package/lib/audit-report.js +93 -0
- package/lib/audit-sync.js +172 -0
- package/lib/audit.js +356 -0
- package/lib/cli-helpers.js +108 -0
- package/lib/commands/accept.js +28 -0
- package/lib/commands/audit.js +17 -0
- package/lib/commands/configure.js +200 -0
- package/lib/commands/doctor.js +14 -0
- package/lib/commands/exceptions.js +19 -0
- package/lib/commands/export-findings.js +46 -0
- package/lib/commands/findings.js +306 -0
- package/lib/commands/ignore.js +28 -0
- package/lib/commands/init.js +16 -0
- package/lib/commands/insights.js +24 -0
- package/lib/commands/install.js +15 -0
- package/lib/commands/list.js +109 -0
- package/lib/commands/remove.js +16 -0
- package/lib/commands/repo.js +862 -0
- package/lib/commands/report.js +234 -0
- package/lib/commands/resolve.js +25 -0
- package/lib/commands/rules.js +185 -0
- package/lib/commands/scan.js +519 -0
- package/lib/commands/scope.js +341 -0
- package/lib/commands/sync.js +40 -0
- package/lib/commands/uninstall.js +15 -0
- package/lib/commands/version.js +33 -0
- package/lib/comment-map.js +388 -0
- package/lib/config.js +325 -0
- package/lib/context-modifiers.js +211 -0
- package/lib/deep-analyzer.js +225 -0
- package/lib/doctor.js +236 -0
- package/lib/exception-manager.js +675 -0
- package/lib/export-findings.js +376 -0
- package/lib/file-context.js +380 -0
- package/lib/file-filter.js +204 -0
- package/lib/file-manifest.js +145 -0
- package/lib/git-utils.js +102 -0
- package/lib/global-config.js +239 -0
- package/lib/hooks-manager.js +130 -0
- package/lib/init-repo.js +147 -0
- package/lib/insights-analyzer.js +416 -0
- package/lib/insights-output.js +160 -0
- package/lib/installer.js +128 -0
- package/lib/output-constants.js +32 -0
- package/lib/output-terminal.js +407 -0
- package/lib/push-queue.js +322 -0
- package/lib/remove-repo.js +108 -0
- package/lib/repo-context.js +187 -0
- package/lib/report-html.js +1154 -0
- package/lib/report-index.js +157 -0
- package/lib/report-json.js +136 -0
- package/lib/report-markdown.js +250 -0
- package/lib/resolve-manager.js +148 -0
- package/lib/rule-registry.js +205 -0
- package/lib/scan-cache.js +171 -0
- package/lib/scan-context.js +312 -0
- package/lib/scan-schema.js +67 -0
- package/lib/scanner-full.js +681 -0
- package/lib/scanner-manual.js +348 -0
- package/lib/scanner-secrets.js +83 -0
- package/lib/scope.js +331 -0
- package/lib/store-verify.js +395 -0
- package/lib/store.js +310 -0
- package/lib/taint-register.js +196 -0
- package/lib/version-check.js +46 -0
- package/package.json +37 -0
- package/rules/rule-loader.js +324 -0
- package/rules/rules-aspx-cs.json +399 -0
- package/rules/rules-aspx.json +222 -0
- package/rules/rules-infra-leakage.json +434 -0
- package/rules/rules-js.json +664 -0
- package/rules/rules-php.json +521 -0
- package/rules/rules-python.json +466 -0
- package/rules/rules-secrets.json +99 -0
- package/rules/rules-sensitive-files.json +475 -0
- package/rules/rules-ts.json +76 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
GNU AFFERO GENERAL PUBLIC LICENSE
|
|
2
|
+
Version 3, 19 November 2007
|
|
3
|
+
|
|
4
|
+
Copyright (C) 2026 Activemind Solutions AB
|
|
5
|
+
Contact: info@activemind.se
|
|
6
|
+
https://activemind.se
|
|
7
|
+
|
|
8
|
+
This program is free software: you can redistribute it and/or modify
|
|
9
|
+
it under the terms of the GNU Affero General Public License as published
|
|
10
|
+
by the Free Software Foundation, either version 3 of the License, or
|
|
11
|
+
(at your option) any later version.
|
|
12
|
+
|
|
13
|
+
This program is distributed in the hope that it will be useful,
|
|
14
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16
|
+
GNU Affero General Public License for more details.
|
|
17
|
+
|
|
18
|
+
You should have received a copy of the GNU Affero General Public License
|
|
19
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
COMMERCIAL LICENSE
|
|
24
|
+
|
|
25
|
+
If you wish to use Secure Code by Design in a proprietary product or
|
|
26
|
+
service, or distribute it as part of a commercial offering without
|
|
27
|
+
complying with the terms of the AGPL-3.0, a commercial license is
|
|
28
|
+
available from Activemind Solutions AB.
|
|
29
|
+
|
|
30
|
+
Contact: info@activemind.se
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
The full text of the GNU Affero General Public License v3 is available at:
|
|
35
|
+
https://www.gnu.org/licenses/agpl-3.0.txt
|
package/README.md
ADDED
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
# Secure Code by Design
|
|
2
|
+
|
|
3
|
+
> Automated security scanning for development teams using AI coding tools.
|
|
4
|
+
|
|
5
|
+
Secure Code by Design (`scd`) is a CLI tool that catches security vulnerabilities before they reach production — running quietly in the background via git hooks and on-demand scans. Built for SMB companies using traditional coding and AI coding tools (Claude Code, GitHub Copilot, Cursor) which generates code faster than their security awareness can keep up with.
|
|
6
|
+
|
|
7
|
+
**Not a replacement for penetration testing.** scd helps you find and fix common vulnerability patterns before they reach production, so that professional security assessments can focus on harder, context-specific problems. It does not replace a penetration test, threat model, or security audit.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **189 security rules** across JavaScript, TypeScript, Python, PHP, ASP.NET, and more
|
|
14
|
+
- **Taint analysis** — tracks user-controlled variables from HTTP input to dangerous sinks
|
|
15
|
+
- **Pre-scan file classification** — files are classified into source, test, and excluded contexts before any rule runs; test files and vendor code are routed separately
|
|
16
|
+
- **Git hooks** – secrets scanning on pre-commit, full OWASP scan on pre-push
|
|
17
|
+
- **Zero repo footprint** – no files written or modified to your repository
|
|
18
|
+
- **Compact terminal output** – summary + top issues + most affected files (use `--verbose` for full detail)
|
|
19
|
+
- **HTML, Markdown and JSON reports** with fix guidance for each finding
|
|
20
|
+
- **Deep analysis** – optional AI-powered analysis via scd-server; requires the Deep Analysis Pack (Premium)
|
|
21
|
+
- **Per-scan storage** – every scan saved with a unique random ID (`s-a3f9b2c1`), never overwritten; regenerate reports from any historical scan
|
|
22
|
+
- **Finding IDs** – every finding gets a stable ID (`f-a1b2c3d4e5`) shown in scan output, reports and export — use directly with `scd accept` and `scd ignore`
|
|
23
|
+
- **Exception management** – reviewed exceptions tracked in config, never as code comments
|
|
24
|
+
- **Exception sync** – pull team-lead approvals from scd-server, sync rejected back with reason
|
|
25
|
+
- **Audit trail** – append-only scan history per repository
|
|
26
|
+
- **`.gitignore` respected** – files git ignores are excluded from scans by default; use `--include-ignored` to override
|
|
27
|
+
- **Scan scope management** – explicitly exclude files, directories, or rules with documented reasons via `scope.yml`; every exclusion is tracked in scan output and audit log
|
|
28
|
+
- **Pipeline-friendly** – works as a subprocess or in CI/CD without a TTY; use `--log-to` to control logging behaviour in automated contexts
|
|
29
|
+
|
|
30
|
+
## Team & Premium
|
|
31
|
+
|
|
32
|
+
scd-server extends the CLI with team collaboration features. When you're ready to move beyond local scanning:
|
|
33
|
+
|
|
34
|
+
- **Team dashboard** — aggregated findings, trend analysis, and knowledge gap tracking across your whole team
|
|
35
|
+
- **Exception approval flow** — developers request exceptions, team leads approve or reject with a reason
|
|
36
|
+
- **CRA Compliance Report** — ready-made documentation for EU Cyber Resilience Act conformity assessments
|
|
37
|
+
- **Findings history** — every scan from every developer in one place, searchable and filterable
|
|
38
|
+
- **Deep Analysis Pack** — AI-powered analysis of CRITICAL and HIGH findings; confirms real vulnerabilities, identifies false positives, and suggests concrete fixes. Your code never leaves your infrastructure.
|
|
39
|
+
|
|
40
|
+
scd-server runs in your own infrastructure. No code, no findings, and no scan data ever leaves your network.
|
|
41
|
+
|
|
42
|
+
See [securecodebydesign.com](https://securecodebydesign.com) for plans and pricing.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Requirements
|
|
47
|
+
|
|
48
|
+
- Node.js 22 or later
|
|
49
|
+
- Git (required for hook installation)
|
|
50
|
+
- npm (included with Node.js)
|
|
51
|
+
|
|
52
|
+
**macOS / Linux:** No additional requirements.
|
|
53
|
+
|
|
54
|
+
**Windows:** Windows 10 (build 1803) or later required. Git for Windows must be installed (not WSL). Windows Terminal or PowerShell recommended — `cmd.exe` has limited ANSI colour support.
|
|
55
|
+
|
|
56
|
+
> On Windows, the store directory is `%USERPROFILE%\\.scd\\` (e.g. `C:\\Users\\YourName\\.scd\\`). All `~/.scd` references in this documentation refer to this path on Windows.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npm install -g @activemind/scd
|
|
64
|
+
scd --version
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
See [INSTALL.md](INSTALL.md) for platform-specific Node.js setup, advanced options, and
|
|
68
|
+
[installation troubleshooting](INSTALL.md#troubleshooting).
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Quick start
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Register a project and install git hooks
|
|
76
|
+
cd /path/to/your/project
|
|
77
|
+
scd init
|
|
78
|
+
|
|
79
|
+
# Run a full security scan
|
|
80
|
+
scd scan
|
|
81
|
+
|
|
82
|
+
# Run with verbose output (full file-grouped + rule-grouped detail)
|
|
83
|
+
scd scan --verbose
|
|
84
|
+
|
|
85
|
+
# Generate an HTML report from the last scan
|
|
86
|
+
scd report
|
|
87
|
+
|
|
88
|
+
# Open the report in your browser
|
|
89
|
+
scd report open # macOS / Windows
|
|
90
|
+
scd report serve # Linux / Firefox (starts local HTTP server)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Commands
|
|
96
|
+
|
|
97
|
+
### Scanning
|
|
98
|
+
|
|
99
|
+
| Command | Description |
|
|
100
|
+
|---|---|
|
|
101
|
+
| `scd scan [target]` | Run a full security scan — vendor and `.gitignore`d files excluded by default |
|
|
102
|
+
| `scd scan --verbose` | Full file-grouped + rule-grouped output |
|
|
103
|
+
| `scd scan --deep` | Deep analysis via scd-server *(Premium)* |
|
|
104
|
+
| `scd scan --include-vendor` | Include vendor/dependency code in scan |
|
|
105
|
+
| `scd scan --vendor-only` | Scan only vendor/dependency code (supply chain) |
|
|
106
|
+
| `scd scan --include-ignored` | Scan files excluded by `.gitignore` (default: respect `.gitignore`) |
|
|
107
|
+
| `scd scan --exclude <pattern>` | Exclude a file or directory for this scan only (repeatable, not saved) |
|
|
108
|
+
| `scd scan --exclude-rule <id>` | Exclude a rule for this scan only (repeatable, not saved) |
|
|
109
|
+
| `scd scan --log-to <mode>` | Logging mode for non-interactive use: `none`, `current`, `target` (see below) |
|
|
110
|
+
| `scd scan --no-sync` | Skip pushing this scan to scd-server (audit log kept locally) *(Premium)* |
|
|
111
|
+
| `scd scan --no-audit` | Skip audit logging entirely for this scan |
|
|
112
|
+
|
|
113
|
+
#### `--exclude` and `--exclude-rule` — one-off scan exclusions
|
|
114
|
+
|
|
115
|
+
Exclude files or rules for a single scan without modifying `scope.yml`. Useful for quick ad-hoc filtering or testing rule changes.
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
scd scan --exclude "tests/fixtures/" --exclude-rule INFRA-002
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
One-off exclusions are shown in scan output, stored in the scan JSON, and never written to disk. For permanent exclusions, use `scd repo scope`.
|
|
122
|
+
|
|
123
|
+
#### `--log-to` — non-interactive and pipeline use
|
|
124
|
+
|
|
125
|
+
When `scd scan` is used as a subprocess or in a CI/CD pipeline without a TTY, it automatically detects the missing terminal and scans without logging — no prompt, no hanging. A single informational line is written to stderr.
|
|
126
|
+
|
|
127
|
+
Use `--log-to` when you need explicit control over logging in non-interactive contexts:
|
|
128
|
+
|
|
129
|
+
| Value | Behaviour |
|
|
130
|
+
|---|---|
|
|
131
|
+
| `none` | Scan without logging, no prompt (same as auto-detect default) |
|
|
132
|
+
| `current` | Log results to the current working directory's repo, no prompt |
|
|
133
|
+
| `target` | Log results to the target repository's repo, no prompt |
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# Automated pipeline — no TTY, no prompt, output file always written
|
|
137
|
+
scd scan ./repo --format json --output results.json
|
|
138
|
+
|
|
139
|
+
# Cron job — scan and log results to current repo
|
|
140
|
+
scd scan . --log-to current
|
|
141
|
+
|
|
142
|
+
# Explicit no-logging
|
|
143
|
+
scd scan . --log-to none
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Normal interactive behaviour (with a TTY and no `--log-to`) is completely unchanged.
|
|
147
|
+
|
|
148
|
+
### Reports
|
|
149
|
+
|
|
150
|
+
| Command | Description |
|
|
151
|
+
|---|---|
|
|
152
|
+
| `scd report` | Generate report from last scan (HTML default) |
|
|
153
|
+
| `scd report open` | Generate report and open in browser |
|
|
154
|
+
| `scd report serve` | Serve report via local HTTP server |
|
|
155
|
+
| `scd report serve --index` | Always show report index page |
|
|
156
|
+
| `scd report --scan <id>` | Generate report from a specific saved scan |
|
|
157
|
+
| `scd export-findings` | Export all findings from a scan to JSON |
|
|
158
|
+
| `scd export-findings --deep-only` | Export only findings that have a deep analysis result |
|
|
159
|
+
| `scd export-findings --severity critical` | Filter exported findings by severity |
|
|
160
|
+
| `scd export-findings --scan <id>` | Export from a specific saved scan |
|
|
161
|
+
|
|
162
|
+
### Findings
|
|
163
|
+
|
|
164
|
+
| Command | Description |
|
|
165
|
+
|---|---|
|
|
166
|
+
| `scd findings` | List open (unhandled) findings from last scan |
|
|
167
|
+
| `scd findings <finding-id>` | Show a specific finding with full detail (problem, scenario, fix) |
|
|
168
|
+
| `scd findings --verbose` | Show all open findings with problem description, scenario, and fix |
|
|
169
|
+
| `scd findings --all` | All findings including excepted and resolved |
|
|
170
|
+
| `scd findings --excepted` | Only excepted findings |
|
|
171
|
+
| `scd findings --show-suppressed` | Show findings suppressed by file context classification |
|
|
172
|
+
| `scd findings --severity critical` | Filter by severity |
|
|
173
|
+
| `scd findings --rule <id>` | Filter by rule ID |
|
|
174
|
+
| `scd findings --scan <id>` | Load a specific historic scan |
|
|
175
|
+
|
|
176
|
+
### Exception and scope management
|
|
177
|
+
|
|
178
|
+
| Command | Description |
|
|
179
|
+
|---|---|
|
|
180
|
+
| `scd accept <finding-id> --reason "<text>"` | Accept a risk — requires team-lead approval via scd-server |
|
|
181
|
+
| `scd ignore <finding-id> --reason "<text>"` | Ignore a finding (false positive, out of scope) |
|
|
182
|
+
| `scd resolve` | Mark an EXPOSURE finding as handled, or remove a rejected exception |
|
|
183
|
+
| `scd sync` | Pull approvals/rejections from scd-server |
|
|
184
|
+
| `scd exceptions` | List all exceptions and their status |
|
|
185
|
+
| `scd scope --show` | Show current scope exclusions |
|
|
186
|
+
| `scd scope --add-file <pattern>` | Permanently exclude a file or directory |
|
|
187
|
+
| `scd scope --add-rule <id>` | Permanently exclude a rule |
|
|
188
|
+
| `scd scope --remove-file <pattern>` | Remove a file exclusion |
|
|
189
|
+
| `scd scope --remove-rule <id>` | Remove a rule exclusion |
|
|
190
|
+
|
|
191
|
+
### Repository management
|
|
192
|
+
|
|
193
|
+
| Command | Description |
|
|
194
|
+
|---|---|
|
|
195
|
+
| `scd init` | Register current directory and install git hooks |
|
|
196
|
+
| `scd repo` | Show repo info and scan statistics |
|
|
197
|
+
| `scd repo configure` | Show or update per-repo configuration |
|
|
198
|
+
| `scd repo hooks --disable --reason "<text>"` | Disable git hooks for this repo (reason required) |
|
|
199
|
+
| `scd repo hooks --enable` | Re-enable git hooks |
|
|
200
|
+
| `scd repo scope` | Manage per-repo scope exclusions |
|
|
201
|
+
| `scd list` | List all registered repos |
|
|
202
|
+
| `scd hooks` | Show hook status across all repos |
|
|
203
|
+
| `scd remove` | Remove repo from scd |
|
|
204
|
+
|
|
205
|
+
### Rules and insights
|
|
206
|
+
|
|
207
|
+
| Command | Description |
|
|
208
|
+
|---|---|
|
|
209
|
+
| `scd rules` | List all rules |
|
|
210
|
+
| `scd rules --search <term>` | Search rules by name or ID |
|
|
211
|
+
| `scd rules --stats` | Show rule counts by severity |
|
|
212
|
+
| `scd insights` | Behavioural analysis across scan history |
|
|
213
|
+
| `scd audit` | Show audit log |
|
|
214
|
+
|
|
215
|
+
### Diagnostics
|
|
216
|
+
|
|
217
|
+
| Command | Description |
|
|
218
|
+
|---|---|
|
|
219
|
+
| `scd doctor` | Verify installation, hook status, and server connectivity |
|
|
220
|
+
| `scd version` | Show version and rule counts |
|
|
221
|
+
| `scd configure --show` | Show server configuration and global defaults |
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Privacy and data handling
|
|
226
|
+
|
|
227
|
+
scd is designed for teams where code privacy is non-negotiable.
|
|
228
|
+
|
|
229
|
+
**Local scanning.** Standard `scd scan` runs entirely on your machine. No code, no findings, no metadata leaves your network. Nothing is sent anywhere unless you explicitly connect scd-server.
|
|
230
|
+
|
|
231
|
+
**scd-server.** When configured, scd pushes scan metadata and findings to your own scd-server instance — running in your own infrastructure. Findings never reach Activemind's servers.
|
|
232
|
+
|
|
233
|
+
**Deep analysis.** `scd scan --deep` sends the triggering code line and 8 lines of surrounding context to your scd-server for AI analysis. The filename, rule ID, and severity are included. Whole files are never sent. Deep analysis requires scd-server with the Deep Analysis Pack. See [securecodebydesign.com](https://securecodebydesign.com) for subscription options.
|
|
234
|
+
|
|
235
|
+
Set `trust_level: maximum_privacy` with `scd repo configure --trust-level maximum_privacy` to disable all external API calls entirely.
|
|
236
|
+
|
|
237
|
+
### Exception management
|
|
238
|
+
|
|
239
|
+
Exceptions are managed by finding ID — shown in scan output, reports, and `scd findings`. Never edit source code comments.
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
# View open findings with their IDs
|
|
243
|
+
scd findings
|
|
244
|
+
|
|
245
|
+
# Accept a risk (requires team-lead approval via scd-server)
|
|
246
|
+
scd accept f-a1b2c3d4e5 --reason "Parameterized internally, validated input only"
|
|
247
|
+
|
|
248
|
+
# Ignore a finding (false positive, out of scope etc.)
|
|
249
|
+
scd ignore f-a1b2c3d4e5 --reason "Source maps intentionally included in staging" \
|
|
250
|
+
--tag false_positive
|
|
251
|
+
|
|
252
|
+
# Pull approvals/rejections from team server
|
|
253
|
+
scd sync
|
|
254
|
+
|
|
255
|
+
# List exceptions and their status
|
|
256
|
+
scd exceptions --list all
|
|
257
|
+
|
|
258
|
+
# Remove a rejected exception after fixing the issue
|
|
259
|
+
scd resolve --rejected exc-mn7k96ml
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Exceptions include a hash of the relevant code line. If the code changes, the exception requires re-approval automatically.
|
|
263
|
+
|
|
264
|
+
After `scd sync`, the next scan shows pending status inline:
|
|
265
|
+
|
|
266
|
+
```
|
|
267
|
+
ℹ 2 exception(s) pending approval – synced recently – run scd sync
|
|
268
|
+
⚠ 1 rejected exception(s) — fix required:
|
|
269
|
+
PHP-INJ-002 WS_addUser.php:10 [exc-mn7k96ml]
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Project configuration
|
|
273
|
+
|
|
274
|
+
Per-repository configuration lives in `~/.scd/repos/{repoId}/config.yml` — outside your repository, alongside scan history and exceptions. This keeps your repository clean and avoids committing security configuration to source control.
|
|
275
|
+
|
|
276
|
+
```yaml
|
|
277
|
+
trust_level: balanced # maximum_privacy | balanced | maximum_analysis
|
|
278
|
+
scan_mode: full # full (with taint analysis) | fast (regex only)
|
|
279
|
+
block_on_critical: true
|
|
280
|
+
block_on_high: true
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Edit with `scd repo configure --<option> <value>` or directly in the file.
|
|
284
|
+
|
|
285
|
+
`trust_level` controls which external connections are permitted:
|
|
286
|
+
|
|
287
|
+
| Value | Behaviour |
|
|
288
|
+
|---|---|
|
|
289
|
+
| `maximum_privacy` | No external API calls. Local model only. Strongest privacy guarantee. |
|
|
290
|
+
| `balanced` | Default. Local model preferred; cloud available as explicit opt-in. |
|
|
291
|
+
| `maximum_analysis` | Cloud provider (Claude API). Maximum analysis depth. |
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## Exporting findings
|
|
296
|
+
|
|
297
|
+
`scd export-findings` produces a self-contained JSON snapshot useful for sharing with an external reviewer:
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
scd export-findings # all findings
|
|
301
|
+
scd export-findings --deep-only # only findings with deep analysis
|
|
302
|
+
scd export-findings --severity critical # filter by severity
|
|
303
|
+
scd export-findings --rule PHP-INJ-001 # filter by rule
|
|
304
|
+
scd export-findings --scan s-a3f9b2c1 # from specific scan
|
|
305
|
+
scd export-findings --output /tmp/review.json
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Multi-machine setup
|
|
311
|
+
|
|
312
|
+
See [INSTALL.md](INSTALL.md) for full platform-specific instructions including Node.js setup for macOS, Linux, and Windows.
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Project structure
|
|
317
|
+
|
|
318
|
+
```
|
|
319
|
+
bin/
|
|
320
|
+
scd.js ← CLI entry point (all scd commands)
|
|
321
|
+
lib/
|
|
322
|
+
commands/ ← One file per scd command (scan, findings, report, …)
|
|
323
|
+
scanner-full.js ← OWASP scanner with taint analysis
|
|
324
|
+
scanner-secrets.js ← Fast secrets scanner (pre-commit)
|
|
325
|
+
file-manifest.js ← Pre-scan file classification (source/test/excluded)
|
|
326
|
+
file-context.js ← Per-file classification with two-layer detection
|
|
327
|
+
context-modifiers.js ← Severity adjustment based on file context + _trace
|
|
328
|
+
comment-map.js ← Per-file line classifier (CODE/COMMENT/MIXED)
|
|
329
|
+
taint-register.js ← Single-file taint tracking engine
|
|
330
|
+
store.js ← Global store path management
|
|
331
|
+
store-verify.js ← Store health checks and cleanup
|
|
332
|
+
scan-cache.js ← Per-scan storage (scans/ directory)
|
|
333
|
+
scan-context.js ← Repo-logging context resolution
|
|
334
|
+
rule-registry.js ← Normalised catalogue of all rules
|
|
335
|
+
config.js ← Config loading, isExcepted(), getRuleAction()
|
|
336
|
+
exception-manager.js ← Exception/ignore create, sync, resolve
|
|
337
|
+
deep-analyzer.js ← Deep analysis via scd-server
|
|
338
|
+
cli-helpers.js ← Shared CLI utilities (warnIfOutdated, openInBrowser, tryFlush)
|
|
339
|
+
output-terminal.js ← Compact + verbose terminal output
|
|
340
|
+
report-html.js ← HTML report generator
|
|
341
|
+
report-markdown.js ← Markdown report generator
|
|
342
|
+
report-json.js ← JSON report generator
|
|
343
|
+
export-findings.js ← Export findings to JSON
|
|
344
|
+
rules/ ← Rule definitions per language
|
|
345
|
+
rule-loader.js ← JSON→runtime compiler (pattern, antipatterns, extensions)
|
|
346
|
+
rules-js.json ← JavaScript/TypeScript rules incl. JS-ERR-002/B/C
|
|
347
|
+
rules-infra-leakage.json ← Infrastructure rules incl. INFRA-001/B/C, 002/B/C
|
|
348
|
+
rules-sensitive-files.json ← Sensitive file rules incl. LOG-004, EXPOSURE-001
|
|
349
|
+
rules-secrets.json ← Secrets detection
|
|
350
|
+
rules-python.json ← Python rules
|
|
351
|
+
rules-php.json ← PHP rules
|
|
352
|
+
rules-aspx.json ← ASP.NET markup rules
|
|
353
|
+
rules-aspx-cs.json ← ASP.NET C# rules
|
|
354
|
+
rules-ts.json ← TypeScript-specific rules
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## Dependencies
|
|
360
|
+
|
|
361
|
+
scd is intentionally lightweight. Keeping the dependency surface small is a deliberate design principle — fewer dependencies means fewer supply chain risks, faster installs, and easier security auditing.
|
|
362
|
+
|
|
363
|
+
| Package | Version | Purpose |
|
|
364
|
+
|---|---|---|
|
|
365
|
+
| [commander](https://github.com/tj/commander.js) | ^14.0.3 | CLI argument parsing and subcommand structure |
|
|
366
|
+
| [semver](https://github.com/npm/node-semver) | ^7.x | Semantic version comparison for server compatibility checks |
|
|
367
|
+
|
|
368
|
+
No other runtime dependencies. Node.js built-in modules handle everything else.
|
|
369
|
+
|
|
370
|
+
Verify at any time:
|
|
371
|
+
|
|
372
|
+
```bash
|
|
373
|
+
npm list # full dependency tree
|
|
374
|
+
npm audit # check for known vulnerabilities
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## Roadmap
|
|
380
|
+
|
|
381
|
+
- `scd deps` – Dependency vulnerability scanning against OSV + CISA KEV feeds (designed, in development)
|
|
382
|
+
- Config-context file classification — distinguishes application config, infrastructure config, and schema/documentation files before rules run; improves precision for YAML and configuration-heavy projects
|
|
383
|
+
- `scd uninstall` – clean removal with store data options
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Disclaimer
|
|
388
|
+
|
|
389
|
+
Secure Code by Design is a static analysis tool designed to help development teams identify common security vulnerability patterns in source code. It is provided as a technical aid, not as a guarantee of security.
|
|
390
|
+
|
|
391
|
+
**scd does not replace professional security testing.** No automated tool can detect all vulnerabilities in a codebase. scd does not perform dynamic analysis, penetration testing, runtime monitoring, or threat modelling. Findings depend on rule coverage, code patterns, and the languages supported — classes of vulnerabilities may exist that scd does not and cannot detect.
|
|
392
|
+
|
|
393
|
+
**No liability for security incidents.** Activemind Solutions AB and the contributors to this project accept no liability for security breaches, data loss, regulatory penalties, or any other damages arising from the use or non-use of this tool, including cases where a vulnerability present in a scanned codebase was not identified by scd. The presence of a passing scan does not constitute a security certification or assurance of any kind.
|
|
394
|
+
|
|
395
|
+
**scd is one layer of defence.** Effective application security requires multiple overlapping measures: secure design, code review, dependency management, penetration testing, monitoring, and incident response. scd is designed to make one of those layers — routine code scanning — easier and more consistent. It is not a substitute for any of the others.
|
|
396
|
+
|
|
397
|
+
If your organisation requires a formal security assessment, contact a qualified security professional or penetration testing firm.
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## Security & responsible disclosure
|
|
402
|
+
|
|
403
|
+
Secure Code by Design is a security tool — and like any software, it may contain vulnerabilities. We encourage security testing of this product and welcome responsible disclosure.
|
|
404
|
+
|
|
405
|
+
**Expected behaviour:** Running `scd scan` on this repository will trigger findings in the rule files themselves — patterns that match injection, hardcoded secrets, and similar issues are present by design as test cases. These are expected false positives when scanning the tool's own source code.
|
|
406
|
+
|
|
407
|
+
**Reporting a vulnerability:** If you discover a genuine security issue, please report it privately to [security@activemind.se](mailto:security@activemind.se). Do not open a public GitHub issue for security vulnerabilities.
|
|
408
|
+
|
|
409
|
+
We aim to acknowledge reports within 2 business days and resolve confirmed issues as quickly as possible. Credit is given to researchers who report valid findings responsibly.
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## About
|
|
414
|
+
|
|
415
|
+
Built by [Activemind Solutions AB](https://activemind.se) — security consulting and penetration testing.
|
|
416
|
+
|
|
417
|
+
> Secure Code by Design is a commercial product. See LICENSE for terms.
|
package/bin/scd.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* scd – CLI entry point
|
|
4
|
+
* Config-aware with audit logging
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { Command } = require('commander');
|
|
8
|
+
|
|
9
|
+
const { warnIfOutdated, openInBrowser, tryFlush } = require('../lib/cli-helpers');
|
|
10
|
+
const { loadConfig, getRepoRoot } = require('../lib/config');
|
|
11
|
+
|
|
12
|
+
const program = new Command();
|
|
13
|
+
|
|
14
|
+
const pkg = require('../package.json');
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
program
|
|
18
|
+
.name('scd')
|
|
19
|
+
.description('Secure Code by Design – automated security scanning');
|
|
20
|
+
|
|
21
|
+
// Handle --version / -V before Commander parses — shows scd version output
|
|
22
|
+
// identical to `scd version`, instead of Commander's default single-line format.
|
|
23
|
+
if (process.argv.includes('--version') || process.argv.includes('-V')) {
|
|
24
|
+
require('../lib/commands/version').showVersion();
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
require('../lib/commands/scan').register(program);
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
require('../lib/commands/install').register(program);
|
|
32
|
+
require('../lib/commands/uninstall').register(program);
|
|
33
|
+
|
|
34
|
+
require('../lib/commands/doctor').register(program);
|
|
35
|
+
|
|
36
|
+
require('../lib/commands/accept').register(program);
|
|
37
|
+
|
|
38
|
+
require('../lib/commands/ignore').register(program);
|
|
39
|
+
|
|
40
|
+
require('../lib/commands/findings').register(program);
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
require('../lib/commands/sync').register(program);
|
|
44
|
+
|
|
45
|
+
require('../lib/commands/exceptions').register(program);
|
|
46
|
+
|
|
47
|
+
require('../lib/commands/resolve').register(program);
|
|
48
|
+
|
|
49
|
+
require('../lib/commands/init').register(program);
|
|
50
|
+
|
|
51
|
+
require('../lib/commands/remove').register(program);
|
|
52
|
+
|
|
53
|
+
require('../lib/commands/report').register(program);
|
|
54
|
+
|
|
55
|
+
require('../lib/commands/audit').register(program);
|
|
56
|
+
require('../lib/commands/insights').register(program);
|
|
57
|
+
|
|
58
|
+
require('../lib/commands/configure').register(program);
|
|
59
|
+
require('../lib/commands/scope').register(program);
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
require('../lib/commands/repo').register(program);
|
|
63
|
+
|
|
64
|
+
require('../lib/commands/version').register(program);
|
|
65
|
+
require('../lib/commands/list').register(program);
|
|
66
|
+
require('../lib/commands/rules').register(program);
|
|
67
|
+
|
|
68
|
+
require('../lib/commands/export-findings').register(program);
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
// ── scd review-rules (Activemind-internal, hidden from scd --help) ───────────
|
|
72
|
+
{
|
|
73
|
+
const { Command } = require('commander');
|
|
74
|
+
const reviewCmd = new Command('review-rules');
|
|
75
|
+
reviewCmd
|
|
76
|
+
.description('Export findings with rule internals for Activemind rule quality review')
|
|
77
|
+
.option('--scan <id>', 'Scan ID to export (default: latest scan)')
|
|
78
|
+
.option('--severity <level>', 'Filter by severity: critical, high, medium, exposure')
|
|
79
|
+
.option('--rule <id>', 'Filter to a specific rule ID')
|
|
80
|
+
.option('--deep-only', 'Export only findings that have a deep analysis result')
|
|
81
|
+
.option('--output <path>', 'Output file path (default: ~/.scd/repos/{id}/exports/scd-review-{scanId}.json)')
|
|
82
|
+
.action(async (opts) => {
|
|
83
|
+
const path = require('path');
|
|
84
|
+
const store = require('../lib/store');
|
|
85
|
+
const { exportFindings } = require('../lib/export-findings');
|
|
86
|
+
const { loadCache } = require('../lib/scan-cache');
|
|
87
|
+
const repoRoot = getRepoRoot();
|
|
88
|
+
|
|
89
|
+
let resolvedScanId = opts.scan || null;
|
|
90
|
+
if (!resolvedScanId) {
|
|
91
|
+
const latest = loadCache(repoRoot);
|
|
92
|
+
if (latest) resolvedScanId = latest.scanId;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const defaultName = 'scd-review-' + (resolvedScanId || 'scan') + '.json';
|
|
96
|
+
const outputPath = opts.output
|
|
97
|
+
? path.resolve(process.cwd(), opts.output)
|
|
98
|
+
: store.exportPath(repoRoot, defaultName);
|
|
99
|
+
|
|
100
|
+
await exportFindings({
|
|
101
|
+
repoRoot,
|
|
102
|
+
scanId: opts.scan || null,
|
|
103
|
+
severity: opts.severity || null,
|
|
104
|
+
rule: opts.rule || null,
|
|
105
|
+
deepOnly: !!opts.deepOnly,
|
|
106
|
+
outputPath,
|
|
107
|
+
includeRuleInternals: true,
|
|
108
|
+
command: 'review-rules',
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
program.addCommand(reviewCmd, { hidden: true });
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
program.parse(process.argv);
|
|
117
|
+
|
|
118
|
+
// ── Push worker – trigger after every command ─────────────────────────────
|
|
119
|
+
// Non-blocking: runs after the CLI command completes.
|
|
120
|
+
// Only active when a central URL is configured.
|
|
121
|
+
// Failures are silent – never affect CLI output or exit code.
|
|
122
|
+
setImmediate(() => {
|
|
123
|
+
try {
|
|
124
|
+
const { getCentralUrl } = require('../lib/global-config');
|
|
125
|
+
const centralUrl = getCentralUrl();
|
|
126
|
+
if (!centralUrl) return;
|
|
127
|
+
|
|
128
|
+
const { flush, queueSize } = require('../lib/push-queue');
|
|
129
|
+
if (queueSize() === 0) return;
|
|
130
|
+
|
|
131
|
+
// Pass repoRoot so meta includes repo and installation identity
|
|
132
|
+
const repoRoot = (() => {
|
|
133
|
+
try { return require('../lib/config').getRepoRoot(); } catch { return null; }
|
|
134
|
+
})();
|
|
135
|
+
|
|
136
|
+
flush(centralUrl, { repoRoot }).catch(() => {});
|
|
137
|
+
} catch {
|
|
138
|
+
// Non-fatal
|
|
139
|
+
}
|
|
140
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const { RESET, BOLD, DIM, RED, GREEN, YELLOW, CYAN } = require('./output-constants');
|
|
2
|
+
/**
|
|
3
|
+
* audit-report.js
|
|
4
|
+
* Human-readable terminal report of the audit log.
|
|
5
|
+
* Shows findings history, exceptions, expired exceptions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { readAuditLog, EVENTS } = require('./audit');
|
|
9
|
+
|
|
10
|
+
async function showAuditReport(repoRoot, limit = 50) {
|
|
11
|
+
const events = readAuditLog(repoRoot, 500);
|
|
12
|
+
|
|
13
|
+
if (events.length === 0) {
|
|
14
|
+
console.log(DIM + '\n No audit log found yet.' + RESET + '\n');
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
console.log(`\n${CYAN}${BOLD}╔══════════════════════════════════════════╗${RESET}`);
|
|
19
|
+
console.log(`${CYAN}${BOLD}║ Secure Code by Design – Audit Report ║${RESET}`);
|
|
20
|
+
console.log(`${CYAN}${BOLD}╚══════════════════════════════════════════╝${RESET}\n`);
|
|
21
|
+
|
|
22
|
+
// ── Stats ────────────────────────────────────────────────────────────────
|
|
23
|
+
const scans = events.filter(e => e.event === EVENTS.SCAN_STARTED);
|
|
24
|
+
const blocked = events.filter(e => e.event === EVENTS.FINDING_BLOCKED);
|
|
25
|
+
const warned = events.filter(e => e.event === EVENTS.FINDING_WARNED);
|
|
26
|
+
const excepted = events.filter(e => e.event === EVENTS.FINDING_EXCEPTED);
|
|
27
|
+
const expired = events.filter(e => e.event === EVENTS.FINDING_EXCEPTION_EXPIRED);
|
|
28
|
+
const scanBlocked = events.filter(e => e.event === EVENTS.SCAN_BLOCKED);
|
|
29
|
+
|
|
30
|
+
console.log(`${BOLD}Summary${RESET}`);
|
|
31
|
+
console.log(` Total scans: ${scans.length}`);
|
|
32
|
+
console.log(` Blocked scans: ${RED}${scanBlocked.length}${RESET}`);
|
|
33
|
+
console.log(` Blocked findings: ${RED}${blocked.length}${RESET}`);
|
|
34
|
+
console.log(` Warned findings: ${YELLOW}${warned.length}${RESET}`);
|
|
35
|
+
console.log(` Excepted findings: ${DIM}${excepted.length}${RESET}`);
|
|
36
|
+
|
|
37
|
+
if (expired.length > 0) {
|
|
38
|
+
console.log(` ${RED}${BOLD}Expired exceptions: ${expired.length} – action required!${RESET}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ── Expired exceptions – these need immediate attention ──────────────────
|
|
42
|
+
if (expired.length > 0) {
|
|
43
|
+
console.log(`\n${RED}${BOLD}⚠️ Expired exceptions – immediate action required${RESET}`);
|
|
44
|
+
console.log(`${RED}${'─'.repeat(50)}${RESET}`);
|
|
45
|
+
for (const e of expired.slice(-10)) {
|
|
46
|
+
console.log(` ${e.timestamp.slice(0, 10)} ${e.rule_id} ${e.file}:${e.line}`);
|
|
47
|
+
console.log(` ${DIM}Exception approved by: ${e.exception_by || 'unknown'}${RESET}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ── Recent blocked findings ───────────────────────────────────────────────
|
|
52
|
+
if (blocked.length > 0) {
|
|
53
|
+
console.log(`\n${BOLD}Recent blocked findings${RESET}`);
|
|
54
|
+
console.log(`${'─'.repeat(50)}`);
|
|
55
|
+
for (const e of blocked.slice(-10)) {
|
|
56
|
+
const date = e.timestamp.slice(0, 16).replace('T', ' ');
|
|
57
|
+
console.log(` ${DIM}${date}${RESET} ${RED}${e.severity}${RESET} ${e.rule_id}`);
|
|
58
|
+
console.log(` ${DIM}${e.file}:${e.line} [${e.git_user}]${RESET}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ── Active exceptions ─────────────────────────────────────────────────────
|
|
63
|
+
if (excepted.length > 0) {
|
|
64
|
+
console.log(`\n${BOLD}Active exceptions (recent runs)${RESET}`);
|
|
65
|
+
console.log(`${'─'.repeat(50)}`);
|
|
66
|
+
|
|
67
|
+
// Deduplicate by rule+file
|
|
68
|
+
const seen = new Set();
|
|
69
|
+
for (const e of excepted.slice(-20)) {
|
|
70
|
+
const key = `${e.rule_id}:${e.file}`;
|
|
71
|
+
if (seen.has(key)) continue;
|
|
72
|
+
seen.add(key);
|
|
73
|
+
const date = e.timestamp.slice(0, 10);
|
|
74
|
+
console.log(` ${DIM}${date}${RESET} ${YELLOW}${e.rule_id}${RESET} ${e.file}`);
|
|
75
|
+
console.log(` ${DIM}Approved by: ${e.exception_by || 'not specified'}${RESET}`);
|
|
76
|
+
}
|
|
77
|
+
console.log(`\n ${DIM}Run 'scd approve --list' for full list${RESET}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ── Recent scan history ───────────────────────────────────────────────────
|
|
81
|
+
console.log(`\n${BOLD}Recent scans${RESET}`);
|
|
82
|
+
console.log(`${'─'.repeat(50)}`);
|
|
83
|
+
for (const e of scans.slice(-5)) {
|
|
84
|
+
const date = e.timestamp.slice(0, 16).replace('T', ' ');
|
|
85
|
+
const outcome = scanBlocked.find(b => b.session_id === e.session_id);
|
|
86
|
+
const icon = outcome ? `${RED}🚫${RESET}` : `${GREEN}✅${RESET}`;
|
|
87
|
+
console.log(` ${icon} ${DIM}${date}${RESET} ${e.hook} ${e.files_count} file(s) [${e.git_user}]`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
console.log('');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = { showAuditReport };
|