Skip to content

security: the three red-steel fixes that apply cleanly to current main - #653

Merged
CueCrux-Myles merged 4 commits into
mainfrom
redsteel/independent-security-fixes
Aug 7, 2026
Merged

CueCrux-Myles merged 4 commits into
mainfrom
redsteel/independent-security-fixes

Conversation

@CueCrux-Myles

Copy link
Copy Markdown
Contributor

First slice of the red-steel remediation stranded in #630. These three commits are taken unmodified from codex/crux-red-steel-remediation-2026-07-30 and cherry-pick cleanly onto current main:

commit fix
a3e2eabf fix(mcp): fail closed for OAuth-only auth
2d5c085d fix(security): harden daemon browser responses
014cd05a fix(security): remove the yanked ZIP crypto path

Verification

  • cargo check --workspace --all-targets — clean
  • cargo test -p crux-mcp -p corecruxd -p corecrux-receipts -p corecruxctl --lib — 282 / 1229 / 801 passed, 0 failed
  • cargo clippy --workspace --all-targets — 17 errors, identical to the main baseline (all pre-existing in rcx-revocation); no new lint debt

Why only three, when five applied cleanly

41ee2515 (test(mcp): assert tenant-isolated actor reads) and 33ec4ed1 (test(observe): scope eviction deletes by tenant) apply textually but are tests for production changes still in the un-rebased set:

  • 33ec4ed1 does not compile — it calls store.delete("default", &id), the tenant-scoped signature introduced by ba642868.
  • 41ee2515 compiles and fails, which is the interesting one. It asserts a work-tenant query_facts sees 2 rows; on current main it sees 3, the extra being a fact written under the default tenant. main and red-steel disagree about whether default-tenant facts are visible cross-tenant, and red-steel's ba642868 / 426ea8a0 treat that as a leak to close.

Both belong with their production commits, not here.

See #630 for the full picture and the remaining slices.

🤖 Generated with Claude Code

CueCrux-Myles and others added 3 commits August 7, 2026 20:58
Use one authentication-posture predicate for transport requests, discovery metadata, and daemon bind validation. Add missing and unknown bearer regressions for OAuth-only deployments.\n\nagent:codex-work
Apply one console-compatible browser security policy to direct daemon routes and the desktop proxy. Deny foreign framing without breaking same-origin Studio and 3D tiles, replace hostile upstream policy headers, and regression-test all response classes plus daemon/proxy parity.

Co-Authored-By: OpenAI Codex <codex@openai.com>
Stop receipt and daemon crates from re-enabling ZIP defaults so the unused aes-crypto graph is absent. Make the CLI Deflate backend explicit with zlib-rs, add a compressed-import round trip, refresh root and fuzz locks, and enforce yanked-package denial in the existing cargo-deny CI job.

Co-Authored-By: OpenAI Codex <codex@openai.com>
`014cd05a` carried its own Cargo.lock from 2026-07-30. Replayed onto a main
that has moved 76 commits since, that lock no longer resolves, and CI runs
`--locked`, so every cargo job failed identically:

  error: the lock file Cargo.lock needs to be updated but --locked was passed

My local verification missed it by running cargo without `--locked`, which
silently regenerated the lock instead of failing. Both workspace and fuzz
lockfiles regenerated and re-checked with `--locked`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@CueCrux-Myles
CueCrux-Myles added this pull request to the merge queue Aug 7, 2026
@CueCrux-Myles

Copy link
Copy Markdown
Contributor Author

Correction to the PR description, on the 41ee2515 note.

I wrote that main and red-steel "disagree about whether default-tenant facts are visible cross-tenant", which implies a filter is being bypassed. That is not the shape of it.

On main the MCP surface has no tenant dimension at all, by acknowledged design:

  • Writestenant_hash_for_write_context(_ctx) ignores its context argument and returns default_tenant_hash() unconditionally. Its own doc comment says so: "Authenticated MCP context has no tenant claim yet, so current deployments resolve to default. When one is added, only this helper needs to change."
  • Readshandle_query_facts and memory_view pass tenant_hash: None, and the filter at facts.rs:733 treats None as no filtering.

So the test sees 3 rows because all three facts land in default and the read is unfiltered — not because isolation is being defeated. ba642868 / 426ea8a0 are completing the documented gap, not fixing a regression.

The residual exposure worth tracking: MCP reads are unfiltered, so facts written under a non-default tenant via other ingest paths (HTTP with a JWT tenant claim) would be returned to an MCP caller.

Nothing in this PR's three commits changes; only my characterisation of why 41ee2515 was excluded.

Merged via the queue into main with commit 63d2749 Aug 7, 2026
29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant