Skip to content

feat(deep): unwrap claude-code envelope and broaden Go authz coverage#48

Merged
boorad merged 4 commits intomainfrom
feat/deep-claude-envelope-and-go-authz-coverage
May 4, 2026
Merged

feat(deep): unwrap claude-code envelope and broaden Go authz coverage#48
boorad merged 4 commits intomainfrom
feat/deep-claude-envelope-and-go-authz-coverage

Conversation

@boorad
Copy link
Copy Markdown
Contributor

@boorad boorad commented May 4, 2026

Summary

Two themes land together because they both came out of dogfooding zift's deep pass against ocp's internal/authz/authz.go:

  • Claude Code transport: users can now point --agent-cmd directly at claude -p --output-format json without a jq -r .result shell wrapper.
  • Go authz coverage: two new structural rules (OPA Rego eval, ABAC access-descriptor builder) plus a filename-priority signal so obvious authz files (internal/authz/authz.go, permissions.py, rbac/check.go, …) survive tight --max-candidates caps in the cold-region pass.

Changes

Deep transport (src/deep/subprocess.rs)

  • Recognise and unwrap the {"type":"result","result":"<stringified-json>",…} envelope emitted by claude -p --output-format json. Inner payload is re-stripped of markdown fences before parsing.
  • Surface envelope-side failures (is_error: true, non-success subtype, missing/null result) as BadResponse so the orchestrator skips that candidate cleanly instead of hard-failing.
  • Defensive Option<String> on the inner result field — a future result: null won't slip through to a confusing parse error.

Cold-region priority (src/deep/candidate.rs)

  • New AUTHZ_PATH_REGEX recognises authz-flavoured path tokens (authz, authn, rbac/abac/acl/iam, permissions?, roles?, polic(y|ies), guards?, access[_-]?control, authoriz/sation, authenticat*, authorit*).
  • Cold-region selection sorts by (priority desc, path asc) so authz-flavoured files always make the budget under tight caps; lex order remains the deterministic tiebreaker.

Go rules (rules/go/)

  • go-opa-rego-eval: matches rego.New(...) — covers both straight Eval and partial-eval flows. (Aliased imports intentionally not matched; documented in the rule.)
  • go-access-descriptor-builder: matches the fluent ABAC builder shape (.WithPrincipal/Subject/Resource/Permission/Role/Tenant/Action). Generic WithName/WithID excluded to keep noise low.

Test plan

  • cargo fmt --check
  • cargo clippy -- -D warnings
  • cargo test — 325 unit + 30 integration pass; new tests cover envelope unwrap (success / is_error / result: null / missing result / unrelated objects), filename priority (positive + negative cases), and cold-region budget tiebreaks.
  • Manual: run zift scan --deep --agent-cmd "claude -p --output-format json" … against ocp and confirm internal/authz/authz.go produces findings without a jq wrapper.

Summary by CodeRabbit

  • New Features

    • Two new Go security rules for authorization patterns: detection of access descriptor builder methods and OPA Rego evaluation call sites.
    • Support for Claude Code's JSON output format wrapper for improved tool integration.
  • Improvements

    • Enhanced file selection ordering to prioritize authorization-related file paths during analysis, improving detection focus and efficiency.

boorad added 4 commits May 4, 2026 17:57
`claude -p --output-format json` wraps the agent's reply in
`{"type":"result","result":"<stringified-findings-json>",...}`. The
subprocess transport tried to deserialise the outer object as
`FindingsEnvelope` directly, which failed at the missing `findings`
field — so every per-candidate analysis was discarded with a
`bad response` warning even when Claude had returned a valid payload
inside `.result`.

Detect the envelope by `type == "result"` plus a string `result`
field, peel one layer (re-stripping any markdown fence the inner text
carries), and parse the inner string as `FindingsEnvelope`. Recognise
`is_error: true` / non-`success` subtype as a real Claude-side
failure and surface as `BadResponse` so the orchestrator's
per-candidate-skip path handles it without a hard fail.

This means users can pass `--agent-cmd "claude -p --output-format json"`
directly without a `jq -r .result` shell wrapper.
Two new high-confidence Go rules that close the gap exposed by
scanning ocp's `internal/authz/` (where the structural pass returned
zero findings on a file literally named `authz.go`):

- `go-opa-rego-eval` — anchors on `rego.New(...)` from OPA's Go SDK,
  the constructor every embedded-OPA flow has to go through. Catches
  both straight-line `.Eval(ctx)` and `.Partial(ctx)` patterns
  without matching bare `.Eval` / `.Partial` on unrelated APIs.

- `go-access-descriptor-builder` — matches fluent builder calls with
  authz-flavoured attribute names (`WithPrincipal`, `WithSubject`,
  `WithResource`, `WithPermission`, `WithRole`, `WithTenant`,
  `WithAction`). Generic builder methods (`WithName`, `WithTimeout`,
  …) intentionally do not match.

Together they take ocp from 0 → 53 structural findings across 8
files, including the previously-invisible `internal/authz/authz.go`.
Cold-region candidate selection used to walk discovered files in
lexicographic order, so under tight `max_candidates` an `app.py`
ahead of `authz.py` could starve the obvious file out of the budget.
This is exactly how ocp's `internal/authz/authz.go` slipped past
both the structural pass (rules gap) and the deep pass (budget gap)
in v0.1.6.

Add `AUTHZ_PATH_REGEX` matching path-segment-bounded tokens
(`authz`, `authn`, `authentication`, `authorization`, `rbac`,
`abac`, `acl`, `iam`, `permission(s)`, `role(s)`, `polic(y|ies)`,
`guard(s)`, `access[_-]control`) and use it as a priority key in
`build_cold_regions`. Files whose path looks authz-flavoured sort
before neutral ones; lexicographic order remains the tiebreaker so
output stays deterministic.

Filenames are deliberately a *priority* signal only — they never
generate findings on their own, since name-only matches conflate
"this file is interesting" with "here's an enforcement point". The
regex is also conservative on boundaries to avoid hits on substrings
like `authoring.md` or `author/`.
Bundles review-feedback polish for the envelope unwrap and cold-region
priority paths:

- Make `ClaudeCodeEnvelope.result` an `Option<String>` so a future
  build emitting `result: null` (or omitting the field on error
  subtypes) surfaces as a clean `BadResponse` instead of falling
  through to a confusing "not valid findings JSON" parse error.
- Include `is_error`, `subtype`, and `result_present` in the envelope
  error message so the operator can see exactly which condition fired.
- Correct the misleading "fails fast on unknown field shape" comment
  (serde ignores unknown fields by default).
- Document the aliased-import limitation on `go-opa-rego-eval`.
- Extend `AUTHZ_PATH_REGEX` to cover the `authorit*` family
  (authority, authoritative, authorities).
- Tighten `cold_region_iterates_authz_paths_first` to pin the lex
  tiebreaker (`app.py` in, `core.py` out) so a future flip is loud.
@boorad boorad self-assigned this May 4, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 4, 2026

📝 Walkthrough

Walkthrough

This PR introduces two new Go authorization rules for detecting ABAC patterns and OPA Rego evaluation sites, improves candidate file selection by prioritizing authorization-flavored paths, and adds Claude Code JSON envelope parsing support to the subprocess client for enhanced output format compatibility.

Changes

Authorization Rules: ABAC and OPA Rego Detection

Layer / File(s) Summary
Rule Definitions
rules/go/access-descriptor-builder.toml, rules/go/opa-rego-eval.toml
Two new Go ABAC rules added: go-access-descriptor-builder matches builder method chains with specific With* patterns; go-opa-rego-eval matches rego.New() call sites for authorization evaluation. Each rule includes Rego template stubs and test fixtures.
Rule Embedding
src/rules/embedded.rs
Both new rules are registered in EMBEDDED_RULES via include_str!() so they are loaded at runtime.

Authorization Path Prioritization in Candidate Selection

Layer / File(s) Summary
Path Heuristic
src/deep/candidate.rs
AUTHZ_PATH_REGEX and path_priority() helper classify file paths as authorization-relevant (priority 1) or neutral (priority 0) using token boundary matching.
Sorting Logic
src/deep/candidate.rs
Cold-region candidates are now sorted by descending path_priority (authz-flavored files first) and then lexicographic path ascending for deterministic tie-breaking, ensuring authz files survive under tight max_candidates constraints.
Tests
src/deep/candidate.rs
New test cases validate path_priority correctness, reject false positives on substring matches, and confirm cold-region iteration prioritizes authz-flavored filenames within budget.

Claude Code Envelope Parsing in Subprocess Client

Layer / File(s) Summary
Envelope Model & Unwrapping
src/deep/subprocess.rs
Added ClaudeCodeEnvelope struct and unwrap_claude_code_envelope() function to parse Claude Code's {"type":"result", "result":"..."} JSON wrapper format, detecting both success and error states.
Subprocess Integration
src/deep/subprocess.rs
Stdout parsing now strips outer markdown fence, attempts envelope unwrapping, and uses the inner stringified JSON (with additional fence stripping) if recognized; otherwise falls back to direct findings parsing. Recognized error envelopes return DeepError::BadResponse instead of attempting findings deserialization.
Tests
src/deep/subprocess.rs
Added unit tests for envelope unwrapping edge cases (success, failure, pass-through) and end-to-end subprocess tests confirming envelope parsing is correctly applied and errors surface as expected.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • EnforceAuth/zift#32: Adds new Go rule TOML files and updates src/rules/embedded.rs to include embedded Go rules, matching the same pattern and integration point.

Poem

🐰 Two ABAC rules now hop in view,
With authz paths blooming bright and true,
Claude Code envelopes gently unwrap,
Deep analysis tightens up the gap!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly summarizes the main changes: Claude Code envelope unwrapping and expanded Go authorization rule coverage. It is concise, specific, and accurately reflects the PR's primary objectives.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@amazon-q-developer amazon-q-developer Bot left a comment

Choose a reason for hiding this comment

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

Review Summary

This PR successfully adds two important features:

  1. Claude Code Transport Support: Adds native support for claude -p --output-format json by unwrapping the transport envelope, eliminating the need for jq shell wrappers.

  2. Enhanced Go Authorization Coverage: Introduces two new structural rules (go-opa-rego-eval and go-access-descriptor-builder) plus filename-based prioritization for authz-relevant files.

Code Quality: The implementation is solid with comprehensive test coverage (325 unit + 30 integration tests), proper error handling, and follows Rust best practices. The defensive parsing approach for the Claude Code envelope is well-designed.

No Critical Issues Found: All changes function correctly and are ready to merge.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/deep/candidate.rs (1)

259-263: ⚡ Quick win

Normalize paths to scan-root-relative before priority sorting.

At lines 260–262, path_priority and lexicographic comparison operate on file.path (absolute). If the checkout directory name contains auth-like keywords (e.g., /tmp/authz-token-abc/src/app.py), spurious prefix matches skew priority and make candidate selection dependent on the local directory structure. Sorting on scan_root-relative paths ensures deterministic, portable results.

♻️ Proposed change
-    discovered.sort_by(|a, b| {
-        path_priority(&b.path)
-            .cmp(&path_priority(&a.path))
-            .then_with(|| a.path.cmp(&b.path))
-    });
+    discovered.sort_by(|a, b| {
+        let a_rel = a.path.strip_prefix(scan_root).unwrap_or(a.path.as_path());
+        let b_rel = b.path.strip_prefix(scan_root).unwrap_or(b.path.as_path());
+        path_priority(b_rel)
+            .cmp(&path_priority(a_rel))
+            .then_with(|| a_rel.cmp(b_rel))
+    });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/deep/candidate.rs` around lines 259 - 263, The sorting currently calls
path_priority and lexicographic compare on absolute paths (discovered.sort_by
closure), which can be skewed by checkout directory names; update the comparator
to first compute scan-root-relative paths (use strip_prefix or a helper like
make_relative(scan_root, &a.path) and same for b.path) and then call
path_priority on those relative paths and compare them lexicographically; if
computing the relative path fails, fall back to the original path to preserve
behavior. Ensure you update the closure where discovered.sort_by is used and
reference path_priority, a.path, b.path and the scan_root variable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/deep/candidate.rs`:
- Around line 259-263: The sorting currently calls path_priority and
lexicographic compare on absolute paths (discovered.sort_by closure), which can
be skewed by checkout directory names; update the comparator to first compute
scan-root-relative paths (use strip_prefix or a helper like
make_relative(scan_root, &a.path) and same for b.path) and then call
path_priority on those relative paths and compare them lexicographically; if
computing the relative path fails, fall back to the original path to preserve
behavior. Ensure you update the closure where discovered.sort_by is used and
reference path_priority, a.path, b.path and the scan_root variable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 434d54f3-fef3-4409-a1a5-80b4ced84f76

📥 Commits

Reviewing files that changed from the base of the PR and between e0b59c6 and 49783c1.

📒 Files selected for processing (5)
  • rules/go/access-descriptor-builder.toml
  • rules/go/opa-rego-eval.toml
  • src/deep/candidate.rs
  • src/deep/subprocess.rs
  • src/rules/embedded.rs

@boorad boorad merged commit c95965d into main May 4, 2026
3 checks passed
@boorad boorad deleted the feat/deep-claude-envelope-and-go-authz-coverage branch May 4, 2026 22:21
This was referenced May 5, 2026
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