[spark-compete wave 5] security cross-cut (stacked)#1465
Merged
Conversation
…ormed-script tolerance, unicode injection defense Consolidates spark-compete Wave-1 input-hardening PRs: - #1432 sanitize module name from git URL (path traversal) — @ifeoluwaaj - #1434 validate column names vs allowlist before SQL interpolation — @ifeoluwaaj - #1423 tolerate malformed package scripts — @Aeyod7 - #1425 unicode normalization in prompt-injection scanner (homoglyph evasion) — @ifeoluwaaj Maintainer completion: added tests/test_prompt_injection_unicode.py covering normalize_unicode + homoglyph-obfuscated injection detection (#1425 headline shipped untested). Co-authored-by: ifeoluwaaj <ifeoluwaaj@users.noreply.github.com> Co-authored-by: Aeyod7 <Aeyod7@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ntity-mutation commands Consolidates spark-compete Wave-1 approval PRs (@mrxlolcat): - #1440 require approval for docker exec, nsenter, chroot (container_privilege_escalation) - #1441 require approval for user/group/credential mutations (identity_access_mutation) adopt_interim: this CLI-surface approval classifier is the still-live gate; on the CLI->harness-core migration it must be re-homed into the Governor approval classifier (authority plane), not left as string matching. No data migration. Maintainer completion: hand-merged #1441's classifier block (line-drift conflict with #1440 in approval.py/test_cli.py) and added tests/test_approval_wave1.py. Co-authored-by: mrxlolcat <mrxlolcat@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ace dedup, SPARK_HOME write-guard, restart/SSH/install fixes Consolidates spark-compete Wave-1 CLI-robustness PRs (resurrected from reviewed-but-unadopted): - #239 accurate error when provider key is managed externally — @mrxlolcat - #241 save partial doctor report even when LLM probe fails — @mrxlolcat - #238 deduplicate trace repair queue entries — @mrxlolcat - #246 exclude SPARK_HOME from write_denied_prefixes (unblocks live start/update) — @mrxlolcat - #81 restart exit code, stop_module PID safety, SSH JSON error, install.sh word-split — @binance1230 - #210 Windows installer preflight when python3 app alias fails — @codex Maintainer completion: 3-way rebased onto current master; stripped the bundled registry.json commit-pin bumps (unauthorized attestation-pin regression). Co-authored-by: mrxlolcat <mrxlolcat@users.noreply.github.com> Co-authored-by: binance1230 <binance1230@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…fallback Consolidates spark-compete Wave-1 CLI-UX PRs (resurrected): - #242 helpful menus for bare 'spark os' / 'spark providers' / 'spark support' — @mrxlolcat - #240 helpful message for bare 'spark recommend' + clarify --desktop help — @mrxlolcat - #283 Android/Termux Desktop fallback when ~/Desktop does not exist — @johncrossu Maintainer completion: applied #240 paired with #242's required=False relax (else the guard is dead code); stripped #283's bundled registry.json bump. Co-authored-by: mrxlolcat <mrxlolcat@users.noreply.github.com> Co-authored-by: johncrossu <johncrossu@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…acing CLI output Consolidates nine spark-compete Wave-1 path-redaction PRs (all @Esc1200) into one coherent pass — credited as a single systemic group (ruleset v2 §5.2), not nine fixes: - #1406 secret file path leak in error message - #1408 redact paths from spawn failure errors - #1421 redact SPARK_HOME from purge safety error - #1422 generic text for manifest_path in SystemExit messages - #1424 redact browser-use print paths (basename reference kept) - #1429 remove internal path leaks from CLI prints - #1430 redact hook/log paths from list output - #1409 redact internal paths from gaps markdown report - #1426 remove operator/log path leaks Maintainer completion: - narrowed _PATH_REDACT_RE to anchor POSIX paths to known roots + ~ + Windows drives, so URL paths (/api/v2/users) and slashy text (and/or, 3/4) are no longer over-redacted; - removed compete-packet-operator-path-leak.json accidentally committed in #1426. Co-authored-by: Esc1200 <Esc1200@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…--lines help, uninstall-feedback + list/output cleanups Consolidates remaining spark-compete Wave-1 CLI-output PRs: - #1428 inspect_builder_event_samples top_trace_refs cap — @4gjnbzb4zf-sudo - #1410 Builder overlap probes report matched count without disclosing the match — @4gjnbzb4zf-sudo - #1407 'spark live logs --lines' help text — @4gjnbzb4zf-sudo - #1427 remove internal module paths from CLI list/status output — @Esc1200 - #1439 preserve uninstall feedback when a named target hits empty registry — @4gjnbzb4zf-sudo Maintainer completion: - #1407/#1410: dropped ALL bundled registry.json commit-pin bumps (unauthorized attestation regression); kept only the cli.py help string / probe_cap fields; - #1427: dropped the leaked trailing module.path column instead of duplicating the name column (the PR's {module.path}->{module.name} swap created a dup); - #1439: hardened args.target access with getattr(args, "target", None). Co-authored-by: 4gjnbzb4zf-sudo <4gjnbzb4zf-sudo@users.noreply.github.com> Co-authored-by: Esc1200 <Esc1200@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ding step 4 PR #243 (resurrect_adopt): two Telegram first-run UX fixes in cli.py. - `spark smoke` with no subcommand now prints a helpful subcommand menu (smoke_command no longer required=True) instead of an argparse error, matching the bare-subcommand menu pattern used elsewhere in the CLI. - Onboarding checklist step 4 now notes that when the LLM key is managed externally (e.g. by a host platform), the user can skip the PING_OK provider test and confirm readiness with `spark providers status`. Co-authored-by: mrxlolcat <mrxlolcat@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ls show non-zero cards build_capability_cards now accepts an optional module_capabilities list and emits a synthetic, explicitly-untrusted card for any module that declares provides_capabilities in spark.toml but has no creator-system or specialization-path surface yet. Wired through build_capability_catalog so a fresh telegram-starter install surfaces non-zero capability cards instead of an empty catalog. Synthetic cards stay trust_status=untrusted / proof_state=proof_incomplete and add no new authority. Salvaged from spark-cli#225 per maintainer review: only the build_capability_cards + build_capability_catalog wiring (~50 lines) is taken. The PR's redact_secret_surface_logs edit (inserts an early return ahead of dead code) and the unrelated build_authority_view path-resolution change are discarded. Co-authored-by: Isaaco3349 <Isaaco3349@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nstall Salvaged the build_memory_review_queue severity/wording hunk from PR #229: when the Builder memory movement export status is "missing"/"" (a fresh install that never had an export), surface it as a "warning" with a "Configure ..." action rather than a "critical" "Restore ..." action that falsely implies a regression. Non-missing failure states still report "critical". Per the maintainer review, only the memory-review-queue hunk was salvaged; the bundled redact_secret_surface_logs early-return rewrite, the build_authority_view spark_home change (already in HEAD), and the trace/capability-card hunks were discarded (covered cleanly by #238/#225 and #266). Co-authored-by: Isaaco3349 <Isaaco3349@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…_health_status Salvaged the build_trace_repair_queue health-status hunk from PR #266: each repair row's current_health_status now reflects that producer's own temporal state ("stale_missing_trace_ref" when the latest known row predates the active window, "latest_clean" when the newest row already carries a trace_ref) instead of always echoing the global trace-window status. Falls through to the global current_health status when neither per-item state applies. Per the maintainer review, only the trace-repair health hunk was salvaged. The PR's seen_ids dedup is already covered in HEAD by the equivalent seen_repair_keys (component, event_type) guard. The build_authority_view spark_home change is already in HEAD; the redact_secret_surface_logs rewrite and capability-card/memory-severity hunks were dropped as out-of-scope (memory severity landed separately from #229). The PR's stray 15-space indentation on this line was corrected to 16. Co-authored-by: Isaaco3349 <Isaaco3349@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…npm subcommand allowlist PR #1419 (ifeoluwaaj): write SSH known_hosts via mkstemp + os.replace so a concurrent reader never observes a truncated/partial host-key file (TOCTOU race). Mirrors the existing atomic-write pattern in ssh_targets persistence. The companion write_env_file newline-sanitization from this PR is already present earlier in the stack, so only the ssh.py atomic write is net-new here. PR #150 (FianKuong): access_lane_payload iterated load_ssh_targets() as if it were a list, but it returns dict[str, SshTarget]; the comprehension was binding `target` to the string key and every `.host_key_status` access would raise. Iterate .values() so trusted-target detection actually works. PR #300 (mrxlolcat): restrict npm in module install commands to the install/ci subcommands only, rejecting arbitrary npm subcommands (run-script, exec, etc.) that could execute attacker-controlled lifecycle hooks. Co-authored-by: ifeoluwaaj <ifeoluwaaj@users.noreply.github.com> Co-authored-by: FianKuong <FianKuong@users.noreply.github.com> Co-authored-by: mrxlolcat <mrxlolcat@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ost_execution, fix support log message PR #187 (4gjnbzb4zf-sudo) + PR #299 (mrxlolcat) — same systemic concern, consolidated: normalize_fixture_finding downgraded embedded-private-key findings to "low" when they appeared on a fixture/test path. A real private key checked into a test file is still a real, exfiltratable key. Drop embedded-private-key from the downgrade set; network-exfiltration and environment-dump fixture downgrades are unchanged. PR #1417 (ifeoluwaaj): add high_cost_execution to APPROVAL_ENFORCED_ACTION_CLASSES. The class was already emitted by the approval classifier (security/approval.py) but was never in the enforced set, so high-cost-execution decisions never actually required approval. Enforce it. harness_core=interim_until_migration: re-home into Governor on migration. PR #236 (mrxlolcat): the support-bundle summary always printed "Logs are excluded unless you used --include-logs" even when --include-logs WAS passed, misleading the user about what the bundle contains. Branch on args.include_logs and warn to review log excerpts when they are included. Co-authored-by: 4gjnbzb4zf-sudo <4gjnbzb4zf-sudo@users.noreply.github.com> Co-authored-by: mrxlolcat <mrxlolcat@users.noreply.github.com> Co-authored-by: ifeoluwaaj <ifeoluwaaj@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ssify uninstall --all, flag chmod/chown + curl/wget file-writes PR #298 (mrxlolcat): approval decisions echoed target_display verbatim into user-facing output, so a secret embedded in a command target (e.g. an inline token) could be printed back. Route target_display through _redact_display and expand SECRET_LIKE_PATTERN to cover GitHub PATs, AWS access keys (AKIA/ASIA), and Slack tokens in addition to the existing OpenAI/Anthropic keys, JWTs, and Telegram bot tokens. PR #245 (mrxlolcat): classify `spark uninstall --all` as destructive_filesystem/high — it removes every installed module and its generated config, which is unrecoverable without reinstalling, but was previously unguarded (only --purge-home required approval). harness_core=interim_until_migration: re-home into Governor on migration. PR #1418 (ifeoluwaaj) — adjusted per maintainer note: - Flag chmod/chown as destructive_filesystem/high (permission/ownership changes enable privilege escalation). - Flag curl/wget that writes downloaded content to disk. Per the note, the wget rule triggers on default-output (wget writes to the cwd with no flag); curl triggers only with -o/--output/-O. - The note's "curl-pipe-to-shell detection" is already covered by the existing remote_code_execution rule, so the new file-write rule is guarded to defer to it (and to the existing upload/exfiltration rule) — it never downgrades those higher-severity classes. - Added unit tests mirroring test_approval_classifier_flags_docker_privilege_escalation: chmod/chown, curl/wget file-write, plain-GET-is-not-file-write, and pipe-to-shell-stays-RCE regression guards. harness_core=interim_until_migration: re-home into Governor on migration. Co-authored-by: mrxlolcat <mrxlolcat@users.noreply.github.com> Co-authored-by: ifeoluwaaj <ifeoluwaaj@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…DPAPI guard, pip-index lock, docker tmpfs, redact-first truncation PR #87 (binance1230) — landed selectively per maintainer note (fixes 1, 2, 3, 5-adjusted, 6 adopted; fix 4 dropped): - Fix 1 (entrypoint.sh): pass the OpenAI key to `codex login` via a 0600 mktemp file + stdin redirect instead of `printenv KEY | codex login`, so the key never appears in the process argument list / `ps` output. The temp file is removed immediately after. - Fix 2 (dpapi_unprotect): a DPAPI-prefixed secret on a non-Windows host used to silently return the still-encrypted blob (caller gets garbage). Restructure so the prefix is checked first, then raise a clear OSError on non-Windows instructing the user to re-enter the secret. (The PR placed this check after the early return, making it dead code; corrected here.) - Fix 3 (install_command_argv): reject custom package-index URLs (--index-url / --extra-index-url) on the pip and `uv pip` install paths via _reject_custom_pip_index, preventing dependency-confusion installs from an attacker-controlled index. - Fix 5 (safe_short_string): keep the broadened redaction keyword set (password/passwd/credential/private_key/auth + Bearer/Basic) but PRESERVE the redact-on-full-string-BEFORE-truncate ordering (the PR reordered to truncate-first, which can leak the leading chars of a long secret). Added a regression test proving truncate-first would have leaked. - Fix 6 (docker-compose.vps.yml): mount /home/spark as a size-limited nosuid tmpfs so the container has a writable home without a persistent on-disk one. Fix 4 (table-name SQL identifier sanitization) intentionally NOT taken here — coordinate with open PR #1434 which handles the same-file SQL-identifier class, to avoid double-handling. The PR's SQL-table-name test is likewise dropped. Tests: pip-index rejection (custom + extra), default-pip passthrough, DPAPI cross-platform raise + non-DPAPI passthrough, and safe_short_string redaction (api_key, Bearer, broadened keywords, redact-before-truncate). Co-authored-by: binance1230 <binance1230@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-out; regen installer manifest Three CI-green code fixes for the wave5 security crosscut: - runtime_policy: the npm subcommand allowlist was placed on the trusted module-boot (runtime/start) path, which legitimately needs `npm run <script>` to launch Node modules. PR #300's hardening was meant for untrusted *install* commands (handled separately in install_command_argv). Allow `run` on the runtime path so module start works again; install/ci stay allowed there too. - cli (scan_module_trust): "never downgrade embedded-private-key" was too broad and broke the redaction-test-fixture carve-out. A bare key in a test file is still flagged critical, but a placeholder key passed straight into a redaction helper (redact/redactText/scrub/...) inside a fixture path is a sample input, not installed key material, so it is downgraded to low again. Real keys in keys.txt and elsewhere remain critical. - installer-manifest.json: install.ps1 was hardened in wave1 (Windows preflight robustness) but the integrity manifest was never regenerated, so the committed sha256 no longer matched. Regenerated from the current script (hash-only diff). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ehavior These tests asserted pre-hardening behavior that was intentionally and correctly changed; updated to assert the new contract (no behavior weakened): - VBS startup script: assert the `%%`-escaped form (vbs_string now escapes `%` so cmd.exe does not re-expand env-var tokens in the launched command). - os capability cards: a module declaring provides_capabilities now surfaces one synthetic, explicitly-untrusted card (wave3 fallback) instead of an empty catalog; assert card_count == 1 with untrusted / proof_incomplete. - bare `spark os`: migrated to a helpful runtime menu (subparsers required=False) that prints subcommands and returns exit code 1 rather than raising an argparse SystemExit; split into a dedicated test for the menu behavior. - stop_module: now does a liveness pre-check (os.kill(pid, 0)) before signalling; mock os.kill so the SIGTERM-then-wait and SIGTERM->SIGKILL escalation paths run. - builder activation note: no longer leaks the internal filesystem path (wave1 output redaction); assert the redacted note and that the path stays out of it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…li.py gitleaks flagged fake secrets used in redaction/secret-drop tests (sk-secret, ui-secret, HF_TOKEN, a null-byte DPAPI blob). No real secrets. Same class as the existing .gitleaksignore entries. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The wave branches were cut before the registry-pin tag-hardening landed on master, so they carried a stale spark-voice-comms pin at refs/heads/main@21a9467 (now drifted), failing 'verify --registry-pins'. Adopt master's immutable tag-pin (refs/tags/spark-pin-2026-06-25@f4783cc). No code change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-security-crosscut
… (acknowledged growth; extraction deferred to fleet-discipline round)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Spark Compete — Wave 5 (security cross-cut), stacked on
spark-compete/wave4-memory-persona-voiceHighest-value spark-cli security set (4 commits). Wave-5 delta only.
Commits
uninstall --all, flag chmod/chown + curl/wget file-writes (guarded so they never downgrade RCE/exfil classes; 12 new tests) [[spark-compete] fix(security): redact secrets from target_display and expand SECRET_LIKE_PATTERN #298, [spark-compete] fix(security): classify 'spark uninstall --all' as destructive_filesystem/high #245, [spark-compete] fix: handle backslash-escaped quotes in env file values #1418]#1417/#1418/#245areinterim_until_migration(re-home approval rules into the Governor on migration).On-merge points
mrxlolcat 175 · ifeoluwaaj 111 · 4gjnbzb4zf-sudo 34 · binance1230 26 · FianKuong 14
Excluded
.github/workflows/codeql.yml, which needs aworkflow-scoped token to push. Land it via its own dependabot PR or with a workflow-scoped push.Verified: 230/16 delta, no churn, no conflict markers, py_compile clean, 12 new tests green (2 pre-existing base-branch test failures, not from this wave). Draft — gated on CI + approval.
🤖 Generated with Claude Code