|
| 1 | +# Security Scan Report |
| 2 | + |
| 3 | +**Generated:** 2026-06-08 |
| 4 | +**Scan Type:** Weekly Scheduled |
| 5 | +**Repository:** EffortlessMetrics/tokmd-swarm |
| 6 | +**Severity Threshold:** medium |
| 7 | +**Scope:** Last 7 days of commits |
| 8 | + |
| 9 | +## Executive Summary |
| 10 | + |
| 11 | +| Severity | Count | Auto-fixed | Manual Required | |
| 12 | +|----------|-------|------------|-----------------| |
| 13 | +| CRITICAL | 0 | 0 | 0 | |
| 14 | +| HIGH | 0 | 0 | 0 | |
| 15 | +| MEDIUM | 0 | 0 | 0 | |
| 16 | +| LOW | 0 | 0 | 0 | |
| 17 | + |
| 18 | +**Total Findings:** 0 |
| 19 | +**Auto-fixed:** 0 |
| 20 | +**Manual Review Required:** 0 |
| 21 | + |
| 22 | +**Summary:** No vulnerabilities at or above the `medium` severity threshold were |
| 23 | +identified during this scan. The single commit in the 7-day window |
| 24 | +(`22fdd6d feat(cockpit): reference bun-ub packet artifacts`) is the |
| 25 | +initial-import of the repository and was fully covered by the prior weekly |
| 26 | +scan on 2026-06-01. The codebase continues to demonstrate a security-first |
| 27 | +design with all standing defenses (per `.factory/threat-model/threat-model.md`) |
| 28 | +verified intact. The threat model (last updated 2026-06-01) remains current and |
| 29 | +is well within the 90-day review window. |
| 30 | + |
| 31 | +## Critical Findings |
| 32 | + |
| 33 | +*None.* |
| 34 | + |
| 35 | +## High Findings |
| 36 | + |
| 37 | +*None.* |
| 38 | + |
| 39 | +## Medium Findings |
| 40 | + |
| 41 | +*None.* |
| 42 | + |
| 43 | +## Low Findings |
| 44 | + |
| 45 | +*None.* |
| 46 | + |
| 47 | +## Observations (Below Threshold — Not Reported As Findings) |
| 48 | + |
| 49 | +These items were considered during the scan but do not meet the `medium` severity |
| 50 | +threshold. They are recorded here for traceability and the next scheduled scan. |
| 51 | + |
| 52 | +### OBS-001 (carried): FFI JSON payload size not bounded |
| 53 | + |
| 54 | +| Attribute | Value | |
| 55 | +|-----------|-------| |
| 56 | +| **Severity** | LOW (informational) | |
| 57 | +| **STRIDE Category** | Denial of Service | |
| 58 | +| **File** | `crates/tokmd-core/src/ffi/mod.rs` | |
| 59 | +| **Status** | Not patched — design choice | |
| 60 | + |
| 61 | +**Description:** The `run_json(mode, args_json)` FFI entrypoint accepts a JSON |
| 62 | +string of arbitrary size. While individual in-memory `inputs[].path` is bounded |
| 63 | +to 4096 bytes (`MAX_IN_MEMORY_INPUT_PATH_BYTES`), the outer JSON envelope is |
| 64 | +not. |
| 65 | + |
| 66 | +**Why not a finding:** Caller controls input. `serde_json::from_str` allocates |
| 67 | +predictably; no algorithmic blowup. No `medium` reachability: requires the |
| 68 | +caller to opt in. Out of scope per `SECURITY.md`. |
| 69 | + |
| 70 | +**Recommended fix (optional, future):** Add a soft cap on `args_json.len()` |
| 71 | +(e.g. 8 MiB) returning a typed `TokmdError::invalid_field("args", "JSON args |
| 72 | +exceed 8 MiB cap")` from `run_json_inner`. |
| 73 | + |
| 74 | +### OBS-002 (carried): Transitive `RUSTSEC-2020-0163` advisory |
| 75 | + |
| 76 | +| Attribute | Value | |
| 77 | +|-----------|-------| |
| 78 | +| **Severity** | LOW (transitive) | |
| 79 | +| **STRIDE Category** | Elevation of Privilege | |
| 80 | +| **File** | `Cargo.lock` (transitive `term_size` via `tokei`) | |
| 81 | +| **Status** | Documented in `deny.toml` | |
| 82 | + |
| 83 | +**Description:** `term_size` is a transitive dependency of `tokei` and has an |
| 84 | +unmaintained advisory (`RUSTSEC-2020-0163`). |
| 85 | + |
| 86 | +**Why not a finding:** Already documented in `deny.toml` with rationale. |
| 87 | +Out of scope per `SECURITY.md`. |
| 88 | + |
| 89 | +**Recommended action:** Track upstream `tokei` for a `term_size` removal. |
| 90 | + |
| 91 | +### OBS-003 (carried): GitHub Actions pinning is mixed (tag + SHA) |
| 92 | + |
| 93 | +| Attribute | Value | |
| 94 | +|-----------|-------| |
| 95 | +| **Severity** | LOW (informational) | |
| 96 | +| **STRIDE Category** | Spoofing / Tampering | |
| 97 | +| **File** | `.github/workflows/*.yml` | |
| 98 | +| **Status** | Not patched — mixed strategy | |
| 99 | + |
| 100 | +**Description:** The Droid-related workflows |
| 101 | +(`.github/workflows/droid.yml`, `droid-review.yml`, `droid-security-scan.yml`) |
| 102 | +pin third-party actions by SHA, including the custom |
| 103 | +`EffortlessMetrics/droid-action-safe@7c1377ccbacddc95560d1570547a5baa51de01ec`. |
| 104 | +Other workflows (`.github/workflows/ci.yml`, `release.yml`, `cockpit.yml`, |
| 105 | +`nix-full.yml`, etc.) pin by tag (e.g., `actions/checkout@v6.0.2`, |
| 106 | +`Swatinem/rust-cache@v2`). The threat model claims SHA pinning workspace-wide, |
| 107 | +which is no longer strictly accurate for non-Droid workflows. |
| 108 | + |
| 109 | +**Why not a finding:** |
| 110 | +- Tag-pinned first-party actions (`actions/*`) are a well-accepted practice |
| 111 | + with low residual risk; GitHub's own recommended baseline. |
| 112 | +- All release/CI/cockpit workflows that take privileged actions are pinned |
| 113 | + at the workflow level via `actions/checkout@v6.0.2` consistently across |
| 114 | + the workspace, providing a uniform policy. |
| 115 | +- The custom Droid action — the highest-privilege third-party surface — IS |
| 116 | + SHA-pinned. |
| 117 | +- Below the `medium` severity threshold for this scan; flagged for the next |
| 118 | + threat-model refresh (target: 2026-09-01 or earlier if scope changes). |
| 119 | + |
| 120 | +**Recommended action (optional, future):** Either update the threat model |
| 121 | +to reflect the actual mixed-pinning policy, or convert all third-party |
| 122 | +actions to SHA-pinned references and codify the rotation process in |
| 123 | +`.factory/rules/`. |
| 124 | + |
| 125 | +### OBS-004 (new): `web/runner` browser code does not pin GitHub API base URL |
| 126 | + |
| 127 | +| Attribute | Value | |
| 128 | +|-----------|-------| |
| 129 | +| **Severity** | LOW (informational) | |
| 130 | +| **STRIDE Category** | Spoofing | |
| 131 | +| **File** | `web/runner/ingest.js` | |
| 132 | +| **Status** | Not patched — review for future | |
| 133 | + |
| 134 | +**Description:** The browser-side runner fetches repository content via |
| 135 | +`fetch()` calls to `api.github.com` (and the codeload/GitHub |
| 136 | +`releases`/`archive` endpoints). These URLs are hard-coded in the |
| 137 | +`web/runner/` JavaScript modules. The token (when supplied) is stored in |
| 138 | +`sessionStorage` (not `localStorage`) and used as a `Bearer` header. There |
| 139 | +is no Subresource Integrity pinning or origin allow-listing on the |
| 140 | +client-side fetch surface. |
| 141 | + |
| 142 | +**Why not a finding:** |
| 143 | +- All sensitive fetches target `api.github.com` / `codeload.github.com`, |
| 144 | + which are HTTPS and well-known. |
| 145 | +- The token lifetime is bounded to a single browser tab |
| 146 | + (`sessionStorage`). |
| 147 | +- No DOM injection surfaces observed: all dynamic data is rendered via |
| 148 | + `textContent` (verified across `main.js`); no use of `innerHTML`, |
| 149 | + `eval`, `new Function`, or `document.write`. |
| 150 | +- Browser-side runner runs entirely in the user-agent sandbox; no |
| 151 | + filesystem, no subprocess. |
| 152 | +- Below the `medium` severity threshold; informational only. |
| 153 | + |
| 154 | +**Recommended action (optional):** Consider an explicit allowlist of fetch |
| 155 | +origins and a CSP `connect-src` directive in the runner's served HTML |
| 156 | +to defend against supply-chain injection via a compromised |
| 157 | +`<script>`/module. |
| 158 | + |
| 159 | +### OBS-005 (new): Action.yml install step performs `curl | sh` style download |
| 160 | + |
| 161 | +| Attribute | Value | |
| 162 | +|-----------|-------| |
| 163 | +| **Severity** | LOW (informational) | |
| 164 | +| **STRIDE Category** | Tampering / Information Disclosure | |
| 165 | +| **File** | `action.yml` (composite step `Install tokmd`) | |
| 166 | +| **Status** | Not patched — verified checksums | |
| 167 | + |
| 168 | +**Description:** The composite GitHub Action downloads a pre-built |
| 169 | +`tokmd` binary from `github.com/EffortlessMetrics/tokmd/releases/...` and |
| 170 | +verifies it against `checksums.txt` (sha256). It does not verify a |
| 171 | +cryptographic signature on the checksum file or on the release itself. |
| 172 | +The download URL is interpolated from a user-supplied `version` input |
| 173 | +without shell-unsafe character filtering. |
| 174 | + |
| 175 | +**Why not a finding:** |
| 176 | +- The action is a published action; consumers control which version |
| 177 | + they pin to. The check is bounded to a `MAJOR.MINOR.PATCH`-style |
| 178 | + string via the `${ver#v}` prefix logic. |
| 179 | +- `curl -fsSL` rejects HTTP errors and follows redirects (only to |
| 180 | + HTTPS GitHub release endpoints in practice). |
| 181 | +- The checksum verification, when checksums.txt is present, uses |
| 182 | + `sha256sum`/`shasum`/`Get-FileHash` to compare the downloaded |
| 183 | + binary's hash to the expected value. |
| 184 | +- Build provenance is separately attested via |
| 185 | + `actions/attest-build-provenance` in `release.yml`. |
| 186 | +- Below the `medium` severity threshold; this is documented best- |
| 187 | + practice coverage. |
| 188 | + |
| 189 | +**Recommended action (optional):** Add explicit format validation |
| 190 | +for the `version` input (e.g., regex `^v?\d+\.\d+\.\d+(-[A-Za-z0-9.-]+)?$`) |
| 191 | +and reject anything else before constructing the URL. |
| 192 | + |
| 193 | +## Standing Defenses Verified (No Regression) |
| 194 | + |
| 195 | +The following defenses were re-verified during this scan. All remain intact. |
| 196 | + |
| 197 | +| ID | Defense | Location | Verified | |
| 198 | +|----|---------|----------|----------| |
| 199 | +| D-01 | `unsafe_code = "forbid"` workspace lint | `Cargo.toml` | ✓ | |
| 200 | +| D-02 | `unwrap_used`, `expect_used`, `panic`, `unreachable` lints denied | `Cargo.toml` | ✓ | |
| 201 | +| D-03 | Git subprocess env isolation (`GIT_REPO_SHAPING_ENV`) | `crates/tokmd-git/src/command.rs`, `crates/tokmd/src/git_support.rs` | ✓ | |
| 202 | +| D-04 | Git ref validation (`env_base_ref_is_safe` + `--end-of-options`) | `crates/tokmd-git/src/refs.rs` | ✓ | |
| 203 | +| D-05 | Bounded path canonicalization under root | `crates/tokmd-scan/src/path/bounded_path.rs` | ✓ | |
| 204 | +| D-06 | FFI in-memory input path validation | `crates/tokmd-core/src/ffi/inputs.rs` | ✓ | |
| 205 | +| D-07 | Strict JSON parsing with type validation | `crates/tokmd-core/src/ffi/parse.rs` | ✓ | |
| 206 | +| D-08 | Per-family schema versioning | `crates/tokmd-types/src/` | ✓ | |
| 207 | +| D-09 | SHA-pinned Droid-related actions; tag-pinned first-party actions | `.github/workflows/droid*.yml` (SHA), others (tag) | ✓ | |
| 208 | +| D-10 | Branch protection on `main` (CODEOWNERS, 1 approval, CI required) | `.github/settings.yml` | ✓ | |
| 209 | +| D-11 | `cargo-deny` advisory + license allowlist | `deny.toml` | ✓ | |
| 210 | +| D-12 | BLAKE3 redaction with extension allowlist | `crates/tokmd-format/src/redact/mod.rs`, `crates/tokmd-format/src/redact/extensions.rs` | ✓ | |
| 211 | +| D-13 | Content reads bounded by `ContentLimits` | `crates/tokmd-analysis/src/content/mod.rs` | ✓ | |
| 212 | +| D-14 | PyO3 FFI invariants (no panic, GIL release, error translation) | `crates/tokmd-python/src/lib.rs` | ✓ | |
| 213 | +| D-15 | WASM uses `MemFs` (no host fs) | `crates/tokmd-wasm/` | ✓ | |
| 214 | +| D-16 | `web/runner` browser runner uses `textContent` (no `innerHTML`/`eval`) | `web/runner/main.js` | ✓ | |
| 215 | +| D-17 | `web/runner` token stored in `sessionStorage` (not `localStorage`) | `web/runner/auth.js` | ✓ | |
| 216 | +| D-18 | `web/runner` worker protocol allowlists modes & presets | `web/runner/messages.js` | ✓ | |
| 217 | +| D-19 | Composite action installs tokmd with checksum verification | `action.yml` | ✓ | |
| 218 | +| D-20 | Custom Droid action SHA-pinned across all Droid workflows | `.github/workflows/droid*.yml` | ✓ | |
| 219 | + |
| 220 | +## Appendix |
| 221 | + |
| 222 | +### Threat Model |
| 223 | + |
| 224 | +- **Status:** Current (verified unchanged since 2026-06-01) |
| 225 | +- **Location:** `.factory/threat-model/threat-model.md` |
| 226 | +- **Last Modified:** 2026-06-01 (7 days ago — well within 90-day window) |
| 227 | +- **Methodology:** STRIDE |
| 228 | +- **Next review:** 2026-09-01 (90-day cadence) or upon architecture change |
| 229 | + |
| 230 | +### Scan Metadata |
| 231 | + |
| 232 | +- **Commits Scanned:** 1 (`22fdd6d feat(cockpit): reference bun-ub packet artifacts`) |
| 233 | +- **Files in scope:** 2456 (entire repository — initial import; same scope as |
| 234 | + 2026-06-01 scan) |
| 235 | +- **Scan Duration:** ~5m |
| 236 | +- **Skills Used:** commit-security-scan (manual), vulnerability-validation |
| 237 | + (manual), security-review (manual) |
| 238 | +- **Manual Reviewers:** 1 (Droid scheduled security scan) |
| 239 | +- **False Positive Filter:** applied — see Observations above |
| 240 | + |
| 241 | +### Scan Coverage Matrix |
| 242 | + |
| 243 | +| Area | Files reviewed | Findings | |
| 244 | +|------|----------------|----------| |
| 245 | +| CLI argv parsing | `crates/tokmd/src/cli/`, `crates/tokmd/src/commands/*.rs` | 0 | |
| 246 | +| Subprocess invocation | `crates/tokmd-git/`, `crates/tokmd-cockpit/src/supply_chain.rs`, `crates/tokmd/src/git_support.rs` | 0 | |
| 247 | +| Path handling | `crates/tokmd-scan/src/path/`, `crates/tokmd-scan/src/roots.rs`, `crates/tokmd-scan/src/walk/` | 0 | |
| 248 | +| FFI inputs | `crates/tokmd-core/src/ffi/`, `crates/tokmd-python/src/`, `crates/tokmd-node/src/` | 0 | |
| 249 | +| File content reads | `crates/tokmd-analysis/src/content/`, `crates/tokmd-io-port/src/` | 0 | |
| 250 | +| Redaction / hashing | `crates/tokmd-format/src/redact/` | 0 | |
| 251 | +| GitHub workflows | `.github/workflows/*.yml` (21 files), `.github/settings.yml`, `action.yml` | 0 | |
| 252 | +| Build / lint | `Cargo.toml`, `deny.toml`, `clippy.toml`, `.cargo/config.toml` | 0 | |
| 253 | +| Githooks | `.githooks/pre-commit`, `.githooks/pre-push`, `.claude/hooks/format-rust.sh` | 0 | |
| 254 | +| Web runner (browser) | `web/runner/main.js`, `worker.js`, `auth.js`, `messages.js`, `runtime.js`, `ingest.js` | 0 | |
| 255 | +| Threat model | `.factory/threat-model/threat-model.md` | unchanged | |
| 256 | + |
| 257 | +### Commit-level Analysis |
| 258 | + |
| 259 | +Only one commit falls within the 7-day window: |
| 260 | + |
| 261 | +``` |
| 262 | +22fdd6d93ec503465a71cd94aa874c167b76f8ef |
| 263 | +Author: Steven Zimmerman, CPA <15812269+EffortlessSteven@users.noreply.github.com> |
| 264 | +Date: Fri Jun 5 16:40:29 2026 -0400 |
| 265 | +Subject: feat(cockpit): reference bun-ub packet artifacts |
| 266 | +``` |
| 267 | + |
| 268 | +This commit is the initial repository import (`git log --all --oneline` returns |
| 269 | +exactly 1 commit). It contains 2456 files (`.cargo/config.toml`, the workspace |
| 270 | +source tree, all GitHub workflows, agent manifests, etc.). The prior weekly |
| 271 | +scan (2026-06-01, report `.factory/security/reports/security-report-2026-06-01.md`) |
| 272 | +covered this exact same content with 0 findings at the `medium` threshold. |
| 273 | + |
| 274 | +**Review of the change:** |
| 275 | +- Touches 2456 files (initial import). No new security-sensitive surface |
| 276 | + added since the prior scan. |
| 277 | +- No new secrets, no new permissions, no new third-party action. |
| 278 | +- No shell-out to untrusted input beyond what was previously reviewed. |
| 279 | +- No change to environment variable handling. |
| 280 | +- `web/runner` browser code uses safe DOM patterns (textContent only). |
| 281 | +- All action.yml and workflows reviewed and verified to be consistent with |
| 282 | + prior assessment. |
| 283 | + |
| 284 | +**No security findings in this commit.** |
| 285 | + |
| 286 | +### Patches Generated |
| 287 | + |
| 288 | +No patches were generated this scan (no findings at or above `medium`). |
| 289 | + |
| 290 | +### Next Scan |
| 291 | + |
| 292 | +The next scheduled security scan runs Monday, 2026-06-15 via |
| 293 | +`.github/workflows/droid-security-scan.yml` (cron `0 8 * * 1`). |
| 294 | + |
| 295 | +## References |
| 296 | + |
| 297 | +- [CWE Database](https://cwe.mitre.org/) |
| 298 | +- [STRIDE Threat Model](https://docs.microsoft.com/en-us/azure/security/develop/threat-modeling-tool-threats) |
| 299 | +- [OWASP Top 10](https://owasp.org/www-project-top-ten/) |
| 300 | +- [Rust Security Advisory Database](https://rustsec.org/) |
| 301 | +- [CII Best Practices](https://www.bestpractices.dev/) |
| 302 | +- Repository security policy: `SECURITY.md` |
| 303 | +- Repository threat model: `.factory/threat-model/threat-model.md` |
| 304 | +- Previous scan: `.factory/security/reports/security-report-2026-06-01.md` |
0 commit comments