Skip to content

v1.0 tag-cut: fail-closed brief-block, HMAC body-parse code, loopback warning (PR 4/4)#16

Closed
tachyon-beep wants to merge 1 commit into
v1.0/03-operator-docsfrom
v1.0/04-code-fixes
Closed

v1.0 tag-cut: fail-closed brief-block, HMAC body-parse code, loopback warning (PR 4/4)#16
tachyon-beep wants to merge 1 commit into
v1.0/03-operator-docsfrom
v1.0/04-code-fixes

Conversation

@tachyon-beep

Copy link
Copy Markdown
Collaborator

Summary

Three pre-tag-cut code fixes with regression tests. Single commit, 3 files, +215/-12 lines.

SEC-01 — Fail-closed entity_briefing_block_reason

crates/clarion-storage/src/query.rs — when serde_json::from_str on a row's properties JSON failed, the function returned None (= unblocked), silently disabling the WP5 briefing block for malformed-JSON rows. Now returns Some("malformed_properties_json"), which routes via the existing http_read.rs:705-712 call site to a 403 BRIEFING_BLOCKED response.

Test: entity_briefing_block_reason_fails_closed_on_malformed_json. Existing tolerant test split to preserve None for valid-JSON-but-non-object shapes (null, []).

CI-02 — HMAC body-parse error code

crates/clarion-cli/src/http_read.rs:426-431 — HMAC middleware body-read failure used to return ErrorCode::InvalidPath ("request body is invalid"), which would mis-route a transport/IO failure as a path-validation failure for a federation client switching on code. Switched to ErrorCode::Internal (500). The outer RequestBodyLimitLayer (line 367) already returns 413 for oversized client bodies, so reaching this inner path implies a real transport/IO failure rather than a normal client request shape.

Test: hmac_middleware_body_read_failure_is_not_invalid_path — asserts the code field is INTERNAL and explicitly NOT INVALID_PATH.

SEC-02 (code half) — Loopback-no-token startup warning

crates/clarion-cli/src/http_read.rs — when both auth_token and identity_secret resolve to None and the bind is loopback, emit a tracing::warn! [TRUST] banner at startup. The operator-facing trust statement landed in PR 3 (docs/operator/secret-scanning.md, docs/operator/clarion-http-read-api.md); this commit adds the visible runtime signal.

Test: spawn_emits_loopback_no_token_trust_warning.

Closes (filigree)

  • clarion-b10e116c04 — SEC-01 fail-closed entity_briefing_block_reason
  • clarion-7dc9fcc72a — CI-02 federation error code on body parse failure
  • clarion-42f4fee904 — SEC-02 (code half — doc half in PR 3)

Test plan

Full CI floor green on this branch:

  • cargo fmt --check / clippy -D warnings / build / nextest run --workspace --all-features (505 passed, 2 skipped) / doc -D warnings / deny check
  • Python plugin: ruff / ruff format --check / mypy --strict / pytest (142 passed)
  • tests/e2e/sprint_1_walking_skeleton.sh
  • CARGO_BUILD=0 tests/e2e/wp5_secret_scan.sh

The 505 vs RC1's 525 delta is the absence of the dogfood-orientation MCP tool tests (a32c162 + 4dd7b63), which remain on the RC1 branch as post-1.0 work.

🤖 Generated with Claude Code

…rning

Three pre-tag-cut code fixes with tests:

- SEC-01 (crates/clarion-storage/src/query.rs): `entity_briefing_block_reason`
  used to return None (= unblocked) when serde_json::from_str on the
  `properties` JSON failed. A plugin emitting malformed properties JSON
  could silently disable the WP5 briefing block. Now returns
  Some("malformed_properties_json") on parse failure; routes to 403
  BRIEFING_BLOCKED via the existing call site at http_read.rs:705-712.

- CI-02 (crates/clarion-cli/src/http_read.rs): HMAC middleware body-read
  failure used to return ErrorCode::InvalidPath ("request body is
  invalid"). Federation clients pattern-matching on `code` would mis-
  route a transport/IO failure as a path-validation failure. Switched
  to ErrorCode::Internal (500) — the closed error envelope does not
  need a new variant; the outer RequestBodyLimitLayer already 413s
  oversized client bodies, so reaching this inner path implies a real
  transport/IO failure rather than a normal request shape.

- SEC-02 code half (crates/clarion-cli/src/http_read.rs): when both
  `auth_token` and `identity_secret` resolve to None and the bind is
  loopback, emit a tracing::warn! [TRUST] banner at startup. The
  operator-facing trust statement was added in the previous commit
  (docs/operator/secret-scanning.md, docs/operator/clarion-http-read-api.md);
  this commit adds the visible runtime signal.

Tests added:
- entity_briefing_block_reason_fails_closed_on_malformed_json
- hmac_middleware_body_read_failure_is_not_invalid_path
- spawn_emits_loopback_no_token_trust_warning

Full CI floor green: cargo fmt/clippy/build/nextest (525 passed)/doc/deny;
ruff/ruff format/mypy --strict/pytest (142 passed); sprint_1_walking_skeleton
and wp5_secret_scan e2e.

Closes gap-register items SEC-01, CI-02, and the code half of SEC-02.
Copilot AI review requested due to automatic review settings May 22, 2026 09:58

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Pre–v1.0 tag-cut hardening for the HTTP read surface and briefing-block enforcement, with regression tests, to avoid silent secret exposure and improve client error routing.

Changes:

  • Fail-closed entity_briefing_block_reason when properties JSON is malformed (treat as briefing-blocked rather than unblocked).
  • Return INTERNAL (500) instead of INVALID_PATH on HMAC middleware body-read failure, with a targeted regression test.
  • Emit an operator-visible startup warning when binding to loopback with neither bearer-token nor HMAC identity configured, with a regression test.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
crates/clarion-storage/tests/query_helpers.rs Splits prior tolerant parsing test and adds a fail-closed malformed-JSON regression test for briefing-block extraction.
crates/clarion-storage/src/query.rs Changes briefing-block reason extraction to return a synthetic block reason on JSON parse failure.
crates/clarion-cli/src/http_read.rs Adds loopback-no-auth startup warning, changes HMAC body-read failure mapping to 500/INTERNAL, and adds regression tests for both.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 293 to +301
/// Extract the `briefing_blocked` reason from an entity's `properties` JSON
/// column. Shared with `clarion-mcp` (which makes the same call inline) so
/// federation read surfaces enforce the block uniformly.
pub fn entity_briefing_block_reason(properties_json: &str) -> Option<String> {
serde_json::from_str::<serde_json::Value>(properties_json)
.ok()?
.get("briefing_blocked")?
.as_str()
.map(str::to_owned)
// Fail-closed: malformed properties JSON treated as briefing-blocked to prevent secret exposure.
let Ok(value) = serde_json::from_str::<serde_json::Value>(properties_json) else {
return Some("malformed_properties_json".to_owned());
};
value.get("briefing_blocked")?.as_str().map(str::to_owned)
Comment on lines 442 to +446
let Ok(body_bytes) = to_bytes(body, HTTP_BODY_LIMIT_BYTES).await else {
// CI-02 fix: a body read failure here is not a path-validation
// problem. The outer `RequestBodyLimitLayer` already rejects
// oversized bodies with the framework's 413; reaching this branch
// means a transport-layer IO failure or a body that could not be
Comment on lines +248 to +250
"[TRUST] HTTP API serving on loopback without authentication; any \
local process on this host can read the catalogue. Set \
identity_token_env or token_env for multi-tenant safety."
Comment on lines +1604 to +1606
let probe = TcpListener::bind(("127.0.0.1", 0)).expect("probe bind");
let bind: SocketAddr = probe.local_addr().expect("probe local addr");
drop(probe);

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8e5804bb7f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 449 to +452
return json_error(
StatusCode::BAD_REQUEST,
ErrorCode::InvalidPath,
"request body is invalid",
StatusCode::INTERNAL_SERVER_ERROR,
ErrorCode::Internal,
"request body could not be read",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Map HMAC body limit errors to 413 instead of INTERNAL

In require_hmac_identity, all to_bytes failures are now forced to 500 INTERNAL, but this branch is also reached for oversized streamed/chunked request bodies on protected routes (where RequestBodyLimitLayer cannot pre-reject via Content-Length). That means a client-side payload-too-large condition is misreported as a server fault, which can trigger incorrect retry behavior and server-error alerting for normal client mistakes. Please special-case length-limit failures here (e.g., 413) instead of classifying every body read error as INTERNAL.

Useful? React with 👍 / 👎.

@tachyon-beep

Copy link
Copy Markdown
Collaborator Author

Superseded — the v1.0 tag-cut work in this stacked chain has all landed on main via the v1.0.1 release path; this branch's tip is fully contained in main. Closing as obsolete. No action needed.

@tachyon-beep
tachyon-beep deleted the v1.0/04-code-fixes branch June 5, 2026 07:32
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.

2 participants