Skip to content

Commit 7e22e65

Browse files
authored
Merge branch 'main' into dependabot/github_actions/actions/deploy-pages-5
2 parents 9424b8b + 7ae9a87 commit 7e22e65

34 files changed

Lines changed: 4787 additions & 700 deletions
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: scanner-selftest
2+
3+
# The gate that makes external rule PRs safely mergeable: installs ripgrep,
4+
# runs the pytest suite, and validates the ruleset + a fixture scan against
5+
# their schemas. Path-filtered to the scanner subproject.
6+
on:
7+
push:
8+
branches: [main]
9+
paths:
10+
- 'dsgai_scanner_tool/**'
11+
- '.github/workflows/scanner-selftest.yml'
12+
pull_request:
13+
paths:
14+
- 'dsgai_scanner_tool/**'
15+
- '.github/workflows/scanner-selftest.yml'
16+
17+
permissions:
18+
contents: read
19+
20+
jobs:
21+
selftest:
22+
runs-on: ubuntu-latest
23+
steps:
24+
- name: Checkout
25+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
26+
27+
- name: Set up Python
28+
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
29+
with:
30+
python-version: '3.11'
31+
32+
- name: Install ripgrep (with PCRE2)
33+
run: sudo apt-get update -qq && sudo apt-get install -y ripgrep
34+
35+
- name: Install dev dependencies
36+
run: python -m pip install --quiet -r dsgai_scanner_tool/requirements-dev.txt
37+
38+
- name: Validate ruleset against its schema
39+
run: |
40+
python -c "import yaml, json, jsonschema; \
41+
jsonschema.validate(yaml.safe_load(open('dsgai_scanner_tool/rules/dsgai-rules.yaml')), \
42+
json.load(open('dsgai_scanner_tool/rules/rules.schema.json'))); \
43+
print('ruleset schema OK')"
44+
45+
- name: Assert compiled JSON is in sync with YAML
46+
run: python dsgai_scanner_tool/build/build_rules_json.py --check
47+
48+
- name: Run the self-test suite
49+
working-directory: dsgai_scanner_tool
50+
run: python -m pytest tests/test_runner.py -q
51+
52+
- name: Validate a fixture scan against the checkpoint + SARIF schemas
53+
working-directory: dsgai_scanner_tool
54+
run: |
55+
python cli/dsgai_scan.py scan tests/fixtures/vulnerable-app \
56+
--json-out /tmp/DSGAI-scan.json --sarif /tmp/scan.sarif --format none || true
57+
python -c "import json, jsonschema; \
58+
jsonschema.validate(json.load(open('/tmp/DSGAI-scan.json')), \
59+
json.load(open('schemas/dsgai-scan.schema.json'))); \
60+
print('checkpoint schema OK')"
61+
python -c "import json; s=json.load(open('/tmp/scan.sarif')); \
62+
assert s['version']=='2.1.0' and s['runs'][0]['tool']['driver']['name']=='dsgai-scan'; \
63+
print('SARIF OK')"

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,10 @@ node_modules/
66
__pycache__/
77
*.pyc
88
.env
9+
10+
# DSGAI scanner generated artifacts
11+
DSGAI-scan.json
12+
DSGAI-scan.sarif
13+
DSGAI-filemap.json
14+
dsgai-baseline.json
15+
dsgai-reports/

dsgai_scanner_tool/CHANGES_v0.3.md

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,67 @@ dates are ISO-8601. The previous line is recorded in [`CHANGES_v0.2.md`](CHANGES
88

99
## [Unreleased]
1010

11+
### Fixed (hard-audit follow-up)
12+
- **CRITICAL — gitignored `.env` files are now scanned.** Discovery honored `.gitignore`,
13+
so the flagship "hardcoded key in `.env`" detection silently didn't run on real repos.
14+
Credential files (`.env*`) are now always scanned.
15+
- **Action exfiltration channel closed.** The narrate job no longer runs an LLM over the
16+
raw untrusted repo with a secret + Bash egress — it renders deterministically via
17+
`cli/dsgai_report.py` (no LLM, no API key in the workflow at all).
18+
- **CVE severity/coverage:** severity computed locally from OSV's CVSS vector (a 9.8 no
19+
longer shows INFO when NVD is down; deterministic); go.mod + package-lock.json parsers
20+
(Go/npm had zero coverage); failed fetches no longer poison the cache.
21+
- **Engine:** `rg` invocations are batched so large repos don't crash the arg limit
22+
(a crash exited 1, masquerading as findings; now exits 2).
23+
- **Coverage:** P16.1 file-existence rule mode makes DSGAI16 PASS reachable; DSGAI20 now
24+
scans `*.js` (unauthenticated JS `/chat` endpoint caught).
25+
- **Packs/docs:** gitleaks catches `AZURE_OPENAI_KEY`/`GCP_*`/`AWS_*` names; Semgrep
26+
export forces `(?m)` for line-anchored rules; the tool-neutral prompt variant no longer
27+
leaks Claude-Code-isms; the pre-commit hook probes for PCRE2 instead of failing open.
28+
- **Report/cleanup:** STRICT report never falls back to a real path on a filemap miss;
29+
line numbers escaped; removed dead code; wired the `run_cve_enrichment` toggle.
30+
1131
### Added
32+
- **Benchmark methodology + announcement drafts** (PR-16 hand-off). `docs/BENCHMARK.md`
33+
(corpus selection, deterministic run steps, a labeling-sheet template, a per-rule
34+
precision snippet, and the responsible-disclosure rule for live findings) and
35+
`docs/ANNOUNCEMENT-DRAFTS.md` (Slack / mailing-list / call-for-reports / lightning-talk).
36+
The benchmark **execution + labeling** and the `scanner-v0.4.0` release remain
37+
maintainer tasks.
38+
- **Ecosystem expansion + rule-pack export** (PR-15).
39+
- **C# / Rust / Ruby**: detection signals (Semantic Kernel/Azure.AI.OpenAI, async-openai,
40+
ruby-openai); CVE manifest parsing for **NuGet** (`*.csproj`), **crates.io**
41+
(`Cargo.lock`), **RubyGems** (`Gemfile.lock`) via OSV; credential coverage extended to
42+
`*.cs`/`*.rs`/`*.rb`/`*.csproj` (13 DSGAI02/13 rules), with C#/Rust/Ruby fixture cases.
43+
- **`build/export_semgrep.py``dist/dsgai.semgrep.yaml`**: exports the 85 STRUCTURAL
44+
rules as a Semgrep pack (value-bearing excluded by design) so incumbent toolchains
45+
carry the DSGAI framework. Generated from the rules YAML; drift is a CI failure.
46+
- **Templated report, single-sourced prompt variant, static ATLAS map** (PR-14).
47+
- `cli/dsgai_report.py` + `templates/report.css`: the HTML report is now rendered
48+
**by code** from the checkpoint (deterministic, testable), with a golden structural
49+
test. The LLM contributes only prose (executive summary / remediation) via `--prose`.
50+
STRICT mode renders file IDs (`F07:12`) and writes `DSGAI-filemap.json`;
51+
accessibility — every status carries a symbol + text label, not colour alone.
52+
- **Sample PNG regenerated** from the fixture app via the new renderer + headless
53+
Chrome (~45 KB, fully reproducible, zero real-repo disclosure).
54+
- `build/generate_prompt_variant.py`: `dsgai_scanner_prompt.md` is now generated from
55+
the skill (frontmatter + `cc-only` blocks stripped) — drift is a CI failure
56+
(`--check`), single-sourced so the two variants can't diverge.
57+
- `rules/atlas-map.yaml`: static MITRE ATLAS technique→control map; the skill's live
58+
`site:atlas.mitre.org` searches are removed in favour of it.
59+
- **CVE pipeline, suppressions, baseline, incremental scanning** (PR-12).
60+
- CVE fetching moved into the CLI (`cli/dsgai_cve.py`, stdlib urllib): OSV
61+
`querybatch` is the per-version source, NVD enriches CVSS by `cveId` only (no
62+
`keywordSearch`). Cached at `~/.dsgai/cve-cache/` (24h TTL, `--refresh-cve`);
63+
online and offline runs are byte-identical. **The LLM never transcribes CVE data.**
64+
- Inline `# dsgai-ignore: P##.# reason="…"` suppressions — surfaced in a visible
65+
`suppressed` section, never silently dropped; a reason is required.
66+
- `baseline` subcommand + `--baseline` — gate only on findings not in the baseline.
67+
- `--diff <ref>` incremental scans (files changed vs a ref), labelled
68+
"INCREMENTAL — not a full assessment".
69+
- New `cve` subcommand; `--exclude`/`--diff`/`--baseline` wired through the skill and
70+
the Action (which now fetches `dsgai_cve.py`). CVE enrichment reaches CI Job 1 with
71+
no WebFetch. `langchain==0.1.0` yields real OSV advisories incl. EXPLOITABLE.
1272
- Contributor infrastructure: `[scanner]` GitHub issue-form templates (false-positive,
1373
false-negative, new-rule, bug), scanner `CONTRIBUTING.md`, public `ROADMAP.md`, and
1474
this changelog scaffold. (PR-01)
@@ -44,10 +104,70 @@ dates are ISO-8601. The previous line is recorded in [`CHANGES_v0.2.md`](CHANGES
44104
(9 tests) asserts every PCRE compiles, the fixture scan matches the answer sheet
45105
exactly, SARIF validity, and the redaction guarantee. `requirements-dev.txt` +
46106
dependabot `pip` for the scanner. (PR-05)
107+
- **Checkpoint schema + CI self-test**: `schemas/dsgai-scan.schema.json` formalizes
108+
`DSGAI-scan.json` and **forbids** `match_text`/`content`/`value`/`raw_grep_output` on
109+
every finding (`"field": false`), making the redaction guarantee machine-checkable.
110+
The CLI self-validates its checkpoint (stdlib) before writing and gained a
111+
cache-invalidation check (`checkpoint_is_valid`: reuse only at current HEAD, clean
112+
tree, matching ruleset). New `.github/workflows/scanner-selftest.yml` installs
113+
ripgrep, runs pytest, and validates the ruleset + a fixture scan against their schemas
114+
— the gate that makes external rule PRs safely mergeable. The PCRE compile check now
115+
keys on rg's exit code (catches PCRE2 errors the old substring check missed). (PR-06)
116+
117+
- **Skill rewrite** (`dsgai_scanner_tool.md`) — the skill is now a CLI-first
118+
orchestrator, not the engine. Adds a mandatory **Trust & Environment Preamble**
119+
(repo content is untrusted; scanner-directed instructions are recorded as a note and
120+
ignored — verified injection-immune on the deterministic path), a **Step 1.5**
121+
CLI-first flow with an in-context fallback and `engine:` header, the value-bearing
122+
**`rg -o --replace ''`** protocol (secret never leaves ripgrep), **STRICT-mode file
123+
IDs** (`F07:12`) with a gitignored `DSGAI-filemap.json`, an **evidence-citation**
124+
requirement (no status without rule IDs + locations), **timestamped reports** under
125+
`dsgai-reports/`, checkpoint cache-invalidation, and `compatible_cli` frontmatter.
126+
(PR-07)
127+
128+
- **Hardened GitHub Action** (`integrations/dsgai-scan.yml`): split into a **`scan`**
129+
job (deterministic CLI only — no secrets, no LLM, no egress; runs on fork PRs; emits
130+
SARIF + checkpoint) and an optional **`narrate`** job (Claude Code with a reduced
131+
toolset `Read,Write,Edit,Bash` — no WebFetch/WebSearch — rendering from the checkpoint;
132+
skipped on forks). Closes the exfiltration channel where untrusted PR content met an
133+
agent holding both secrets and egress. All actions pinned by full SHA; scanner fetched
134+
from a pinned upstream commit; SARIF uploaded via Code Scanning (guarded off forks).
135+
Fixed the push-gate bug — gating is now driven by the `DSGAI_FAIL_ON` repo variable
136+
(empty = report-only), not force-gated on every push. (PR-08)
137+
138+
- **README truth pass**: reflects the deterministic-CLI + LLM-orchestration architecture,
139+
two engine modes, SARIF/Code Scanning, timestamped reports, file-ID strict mode, and
140+
a **Cost & runtime** section ($0 CLI-only mode, fork-PR behavior, `--diff` marked
141+
v0.4). Adds a Contributing quick-start ("found a wrong result? that's a contribution").
142+
Skill version badge → v0.3. (PR-09)
143+
144+
- **Pre-commit: gitleaks pack + portable fallback** (PR-10). New
145+
`integrations/gitleaks/dsgai.toml` — a gitleaks rule pack covering the DSGAI
146+
credential set (quote-optional named assignments + raw token prefixes: Slack `xoxb-`,
147+
GitHub `ghp_`/`github_pat_`, Google `AIza`, AWS `AKIA`, Anthropic/OpenAI project keys,
148+
JWT) with an allowlist for `tests/fixtures/**`, lockfiles, minified JS, and snapshots.
149+
`pre-commit-hook.md` now recommends gitleaks as primary. The bespoke
150+
`dsgai-secret-scan.sh` fallback is fixed for portability: `mapfile` → a bash-3.2-safe
151+
`while read -d ''` loop, `grep -zE` → a `case` filter, quote-optional pattern (catches
152+
the unquoted-`.env` miss), plus a token-prefix branch — shellcheck clean.
47153

48154
### Changed
155+
- Honest-language pass across the skill **and README**: every "safe to share/commit/store"
156+
replaced with "designed to minimize disclosure" + a residual-risk note — the Phase-2
157+
overclaim gate now passes repo-wide. (PR-07, PR-09)
158+
- Sample-report image caption now states it is interim and will be regenerated from the
159+
public fixture app once the deterministic report template lands (PR-14). (PR-09)
49160
- `DSGAI-samplereport.png` compressed from ~5.0 MB to ~0.35 MB (14×) as an interim fix;
50161
full regeneration from the fixture app lands in PR-09. (PR-02)
51162

52163
### Fixed
53-
- _nothing yet_
164+
- **Confirmed false negative** (unquoted `.env` key): P02.1–P02.5 and P13.4 are now
165+
quote-optional, and a new **P02.9** catches raw token literals (`sk-proj-`, `sk-ant-`,
166+
`ghp_`, `github_pat_`, `xox[baprs]-`, `AIza`, `AKIA`, JWT) assigned to *any* variable
167+
name. The fixture `.env` and the JS `xoxb-` token are now caught. (PR-11)
168+
- **Confirmed false positive** (innocent webhook flagged as LLM SQL injection): **P12.1**
169+
rewritten to LLM-signal variable names and gated on an LLM call within 30 lines
170+
(`requires_nearby.pattern`, confidence `medium`). `webhook.py` no longer fires;
171+
`sql_agent.py` still does. Both Appendix A commands verified. The CLI gained
172+
`requires_nearby.pattern` support and a `drop` outcome for corroborating-signal rules.
173+
Zero `known_bug` markers remain in the answer sheet. (PR-11)
-307 KB
Loading

0 commit comments

Comments
 (0)