Skip to content

fix(security,api): opaque prompt IDs and CardDAV single-decode - #1206

Open
seonghobae wants to merge 34 commits into
developfrom
goal/carddav-path-traversal-decode
Open

fix(security,api): opaque prompt IDs and CardDAV single-decode#1206
seonghobae wants to merge 34 commits into
developfrom
goal/carddav-path-traversal-decode

Conversation

@seonghobae

@seonghobae seonghobae commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Current authority — 2026-09-10

This branch remains the Naruon owner for its opaque Prompt API identity, CardDAV RFC 6764 TXT path handling, and admin-auth/source-delegation repairs. It does not own DAV route or workspace-document organization implementation.

  • protected base: develop@042b0c70531b229af3acbd0421a2f23098d848b3
  • exact head: 80a56bee03dd04c3605ae58c39c86d8d9f74f935
  • branch: goal/carddav-path-traversal-decode
  • lifecycle: Ready state but not merge-authorized; current-head CodeQL/required review failures remain

Prompt API / naming contract

Public PromptResponse exposes opaque prompt_uid and omits the sequential persistence id. backend/tests/test_prompt_response_naming_contract.py preserves that semantic boundary; persistence remains unchanged.

CardDAV TXT path contract

The RFC 6764 TXT path parser separates validation representation from execution-wire representation. It rejects malformed percent triplets, nested second interpretations, traversal, backslashes, absolute-URI syntax, query/fragment delimiters, invalid UTF-8, and Unicode controls. It preserves encoded RFC 3986 reserved characters in the execution URI so %2F or %3B data is not silently turned into structural delimiters. RED 43467ed10fb3ec6c3f2075a38acf9a934342ca02, causal fix 882c1dda08276d11bb346774dcc3119f64bc51b9, and doctoring head 80a56bee... retain that repair.

Admin authentication / source delegation

HMAC remains a compatibility credential without admin-membership authority. Admin claims require the verified OIDC/JWKS path (or explicit server/test authority). The legacy None == None delegation edge is fixed by requiring a concrete resource organization before non-owner admin delegation.

Corrected responsibility boundary

The previous body incorrectly named #1345 as the live canonical owner for workspace-document organization isolation and DAV route normalization/capability truth. Fresh succession audit supersedes that statement:

This CardDAV-discovery branch must not copy or restack those Naruon DAV/data owner sources merely because the security vocabulary is adjacent.

Fresh exact-head evidence

For unchanged 80a56bee..., repository-owned runs now read:

  • Application CI 34072589119: success
  • Bandit Security Scan 34072589097: success
  • Docker 34072589271: success
  • Security Scan 34072589117: success
  • SAST Semgrep 34072589089: success
  • CodeQL PR 34072589095: failure

Current OpenCode review is CHANGES_REQUESTED because same-head required checks include CodeQL compatibility failure, Required Noema Review failure, and cancelled Strix evidence. There is no qualifying current-head formal approval that overrides those gates. Do not treat resolved historical threads or predecessor reviews as approval.

Merge boundary

Do not merge until the unchanged exact head has every then-live required context terminal-success, zero valid unresolved current-head findings, and qualifying independent approval. The corrected owner map above is PR-state repair only; it does not authorize cross-owner source adoption.

No predecessor-evidence transfer, self-approval, administrative bypass, ruleset weakening, force-push, destructive rebase, dummy requeue, direct-provider workaround, or model/bot verdict fabrication.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 966047a6-ef70-4d39-8021-0808f06f39ad

📥 Commits

Reviewing files that changed from the base of the PR and between cb55a7e and aba2a03.

📒 Files selected for processing (5)
  • backend/api/security.py
  • backend/tests/test_carddav_discovery.py
  • backend/tests/test_prompt_response_naming_contract.py
  • backend/tests/test_security_source_policy_admin_roles.py
  • docs/doctoring/prompt-response-semantic-identifiers.md
💤 Files with no reviewable changes (1)
  • backend/tests/test_carddav_discovery.py

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


Important

Approval pending

CodeRabbit has no unresolved comments, but it could not review the latest commit because the review limit was reached. Follow the review guidance in this comment to continue.

📝 Walkthrough

Walkthrough

The changes update administrative role authorization, enforce strict CardDAV TXT path canonicalization, and remove the database id from public prompt responses. Tests cover OIDC and HMAC roles, unsafe CardDAV paths, Unicode controls, and prompt response fields.

Changes

Administrative authorization roles

Layer / File(s) Summary
Admin session authority
backend/api/auth.py, backend/tests/test_auth_oidc_admin_roles.py
OIDC sessions can provide administrative roles. HMAC sessions reject those roles. Tests cover all administrative roles and verifier context.
Admin source-policy scope
backend/api/security.py, backend/tests/test_security_source_policy_admin_roles.py
Source policies include system_admin and platform_admin. Tests verify matching-organization access and denial for mismatched or absent organization scope.

CardDAV path canonicalization

Layer / File(s) Summary
Strict TXT path validation
backend/services/carddav_discovery.py, docs/doctoring/carddav-txt-path-canonicalization.md
TXT paths are decoded once with strict UTF-8 handling. Malformed, nested, traversal, separator, URI, query, fragment, and Unicode control values are rejected.
CardDAV path regression coverage
backend/tests/test_carddav_discovery.py, backend/tests/test_carddav_encoded_path_canonicalization.py, backend/tests/test_carddav_unicode_controls.py
Tests cover unsafe encoded paths, malformed escapes, invalid UTF-8, Unicode controls, safe Unicode paths, and literal percent values.

Prompt response contract

Layer / File(s) Summary
Prompt public response
backend/api/prompts.py, backend/tests/test_prompts_api.py, backend/tests/test_prompt_response_naming_contract.py, docs/doctoring/prompt-response-semantic-identifiers.md
Prompt responses no longer declare id. Creation and listing responses continue to return prompt_uid. Runtime serialization and JSON schema tests enforce the contract.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant OIDCVerifier
  participant AuthAuthorization
  participant SourcePolicy
  Client->>OIDCVerifier: Submit verified OIDC session
  OIDCVerifier->>AuthAuthorization: Provide admin role and context
  AuthAuthorization->>SourcePolicy: Evaluate role and organization scope
  SourcePolicy-->>Client: Allow or deny access
Loading

Merge Risk: 🟡 Moderate · up to aba2a

This PR tightens CardDAV path handling, prompt identifiers, and admin authorization, but the current head is not ready to merge while required CI and security checks remain pending or non-passing.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies two primary changes: opaque prompt IDs and CardDAV single-decode validation. It is concise and directly related to the pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch goal/carddav-path-traversal-decode

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@seonghobae

Copy link
Copy Markdown
Contributor Author

Temporarily closing #1206 to reach open-PR count 0 under permanent robot-review/CI gate blockage after honest remediation. Head commit cf5a1b0 on goal/carddav-path-traversal-decode is green locally (prompts opaque id, CardDAV encoded path traversal, document org scope, SMTP CRLF). Re-open same branch when OpenCode/CodeRabbit can approve without gate weaken.

@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

PR governance metadata gate is not ready for aba2a03f3ca87914fcf1ca1c751b173097852bca:

  • Required check strix is CANCELLED on the current head.

coderabbitai[bot]

This comment was marked as resolved.

@opencode-agent
opencode-agent Bot disabled auto-merge August 2, 2026 07:24
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 3, 2026

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Current-head security revalidation completed: opaque prompt identifiers, organization-scoped document access, header-injection guards, and bounded decoded CardDAV path validation are covered by focused regression tests. Refresh central review evidence for this exact head.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Please re-review current head c200f23e5e70f32dc532d959837c12c0a40202a2. The Unicode-control and same-workspace organization-isolation findings are addressed and resolved; the byte-input note remains intentionally unchanged per your guidance.

@seonghobae seonghobae reopened this Aug 29, 2026

Copy link
Copy Markdown
Contributor Author

@opencode-agent Re-run authenticated review for exact current head cb55a7eda5152fe2250c7eb1bd416911b59a5e43. Use the configured NVIDIA_NIM_API_KEY execution path only; do not use COPILOT_GITHUB_TOKEN. Current coverage-evidence is terminal-success, while the required opencode-review is correctly fail-closed because no current-head OpenCode verdict is attached. Predecessor-head, deterministic fallback, synthetic, or model-unavailable fallback evidence must not satisfy this request.

Copy link
Copy Markdown
Contributor Author

Exact-head Strix dependency triage

Current head remains cb55a7eda5152fe2250c7eb1bd416911b59a5e43; the source branch has not moved since 2026-08-26 08:59:14Z, so this is not a stale-tree diagnosis.

The current required Strix run (33228158442, job/check 99035816410) fails before the first model response. The bounded attempts reach the trusted contextual-orchestrator sidecar, then the OpenAI Agents client request is rejected with 400 invalid_stream_options: stream_options.include_usage=true is not supported on /v1/chat/completions. Strix correctly fails closed as provider-unavailable; zero emitted findings are therefore incomplete evidence and must not be treated as a pass.

Responsibility boundary: this is a central OpenAI-compatible gateway/Strix integration defect, not a naruon CardDAV/auth/prompt source defect. The actively moving ContextualWisdomLab/contextual-orchestrator#901 lane already changes tests/test_chat_stream_options_http_honesty.py from fail-closed to accepted and updates server.py to support include_usage=true with a final usage SSE chunk. Do not duplicate or race that central writer from this branch.

Next safe action: keep #1206 unchanged and blocked; after the central compatibility change is merged/published into the Strix sidecar, obtain a fresh exact-head Strix run on cb55a7ed…. Only terminal-success from the then-live required workflow counts. opencode-review and the qualifying independent non-author human approval must also be current and passing before merge. No rerun against the unchanged incompatible gateway, protection bypass, or model/bot approval substitution.

Copy link
Copy Markdown
Contributor Author

@opencode-agent review

Review exact current head cb55a7eda5152fe2250c7eb1bd416911b59a5e43 only. Coverage evidence on this head is terminal-success; predecessor verdicts do not transfer. Use the authenticated OpenCode dispatch path backed by NVIDIA_NIM_API_KEY and do not use COPILOT_GITHUB_TOKEN. Post a formal current-head APPROVED or CHANGES_REQUESTED verdict; deterministic/model-unavailable fallback evidence is non-passing.

@devin-ai-integration devin-ai-integration 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.

Devin Review found 4 new potential issues.

⚠️ 1 issue in files not directly in the diff

⚠️ Literal percent escapes corrupt attachment names

For MIME filenames containing percent triplets, _safe_filename decodes literal text already normalized by the email parser. Changed extensions can select the wrong attachment parser.

Devin Review

Comment thread backend/services/carddav_discovery.py Outdated
Comment thread backend/api/security.py
Comment thread backend/tests/test_carddav_discovery.py Outdated
coderabbitai[bot]
coderabbitai Bot previously approved these changes Sep 1, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 5, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-05T05:39:25.290166Z aba2a03 Draft marked ready
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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: aba2a03f3c

ℹ️ 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 thread backend/services/carddav_discovery.py
@seonghobae seonghobae added the bug Something isn't working label Sep 7, 2026 — with ChatGPT Codex Connector

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Please review exact current head 80a56bee03dd04c3605ae58c39c86d8d9f74f935, especially the reserved-percent CardDAV path repair. Do not transfer predecessor approval or finding state.

@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo='ContextualWisdomLab/naruon'
expected='80a56bee03dd04c3605ae58c39c86d8d9f74f935'

echo '== Exact commit =='
actual="$(git rev-parse HEAD)"
printf 'expected=%s\nactual=%s\n' "$expected" "$actual"
git status --short
git show -s --format='commit=%H%nparents=%P%nsubject=%s' HEAD

echo '== Scope against current develop merge base =='
base="$(git merge-base HEAD develop)"
printf 'merge_base=%s\n' "$base"
git diff --name-status "$base"...HEAD
git diff --stat "$base"...HEAD

echo '== Full changed diff =='
git diff --unified=55 "$base"...HEAD

echo '== CardDAV structure and relevant implementation =='
ast-grep outline backend/services/carddav_discovery.py --items all --match '_txt_context_path|discover|CardDAV' || true
rg -n -C 30 \
  'def _txt_context_path\b|unquote|percent|reserved|decoded|category\(|urljoin|urlparse|path' \
  backend/services/carddav_discovery.py || true

echo '== CardDAV focused test inventory and contents =='
fd -a '^test_.*carddav.*\.py$' backend/tests -x sh -c '
  echo "---- $1"
  ast-grep outline "$1" --items all || true
  rg -n -C 16 "txt_context_path|percent|reserved|encoded|decode|traversal|control|U\+0085|%25" "$1" || true
' sh {}

echo '== Other changed security controls and regression assertions =='
for file in backend/api/data.py backend/api/emails.py backend/api/prompts.py backend/api/auth.py backend/api/security.py backend/tests/test_emails_api.py backend/tests/test_prompts_api.py backend/tests/test_data_api_document_org_isolation.py; do
  if [ -f "$file" ]; then
    echo "---- $file"
    rg -n -C 8 \
      'organization_id|reject_crlf|in_reply_to|references|PromptResponse|prompt_uid|admin|JWKS|source_policy|assert_not_called|status_code\s*==\s*422' \
      "$file" || true
  fi
done

echo '== Temporary workflow check =='
fd -a '^pr-1206-document-org-filter\.yml$' .github/workflows 2>/dev/null || true

echo '== Pull request state, checks, and reviews =='
gh pr view 1206 --repo "$repo" \
  --json headRefOid,baseRefOid,isDraft,mergeStateStatus,statusCheckRollup,reviews \
  --jq '{headRefOid,baseRefOid,isDraft,mergeStateStatus,nonSuccessChecks:[.statusCheckRollup[]? | select(.status != "COMPLETED" or (.conclusion != "SUCCESS" and .conclusion != "SKIPPED" and .conclusion != "NEUTRAL")) | {name,status,conclusion,detailsUrl}],reviews:[.reviews[]? | {author:.author.login,state,commit:.commit.oid}]}' \
  || true

echo '== Unresolved review threads =='
gh api graphql -F owner='ContextualWisdomLab' -F name='naruon' -F number=1206 -f query='
query($owner: String!, $name: String!, $number: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        nodes {
          isResolved
          comments(first: 1) {
            nodes { author { login } path line body }
          }
        }
      }
    }
  }
}' --jq '[
  .data.repository.pullRequest.reviewThreads.nodes[]
  | select(.isResolved == false)
] | {unresolved_count:length,threads:map({author:.comments.nodes[0].author.login,path:.comments.nodes[0].path,line:.comments.nodes[0].line,body:.comments.nodes[0].body})}' || true

Length of output: 171243


@seonghobae I reviewed exact head 80a56bee03dd04c3605ae58c39c86d8d9f74f935.

I found no new blocking source issue.

The CardDAV reserved-percent repair is correct in static review.

  • _txt_context_path uses the fully decoded path for validation.
  • It rejects malformed encoding, nested encoding, traversal, backslashes, URI delimiters, and Unicode Cc controls.
  • _execution_path_preserving_reserved_escapes preserves encoded RFC 3986 reserved characters for execution.
  • An encoded leading / becomes the required structural path delimiter.
  • The regression tests cover preserved %2F and %3B values, nested encodings, invalid UTF-8, malformed triplets, Unicode paths, and encoded literal percent values.

The current tree also includes the prior email-header regression coverage. The tests inject both CR and LF into in_reply_to and references. Each test expects HTTP 422 and verifies that send_email was not called.

PromptResponse does not expose the sequential prompt id. The current diff has no temporary workflow. GitHub reports zero unresolved review threads.

I cannot approve yet. The exact-head required checks are queued or in progress, including backend, CodeQL, scope checks, and image validation. GitHub reports the merge state as BLOCKED.

You are interacting with an AI system.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

OpenCode could not approve from deterministic current-head evidence because GitHub Checks have failed.

Findings

1. HIGH Current-head GitHub Checks - Fix failed required checks before approval

  • Problem: Failed same-head checks remain for 80a56bee03dd04c3605ae58c39c86d8d9f74f935.
  • Root cause: The model-unavailable evidence fallback is allowed only when peer GitHub Checks are complete and clean.
  • Fix: Read and fix the failed check logs below, then rerun the current-head checks.
  • Regression test: Keep the model-unavailable fallback gated on an empty failed-check rollup.

Failed checks:

Changed-File Evidence Map

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Backend: auth.py (4 files)"]
  S1 --> I1["API and service runtime"]
  I1 --> R1["Review risk: Backend: auth.py (4 files)"]
  R1 --> V1["backend tests"]
  Evidence --> S2["Test: test_auth_oidc_admin_roles.py (7 files)"]
  S2 --> I2["regression suite"]
  I2 --> R2["Review risk: Test: test_auth_oidc_admin_roles.py (7 files)"]
  R2 --> V2["targeted test run"]
  Evidence --> S3["Docs: carddav-txt-path-canonicalization.md (2 files)"]
  S3 --> I3["operator or user guidance"]
  I3 --> R3["Review risk: Docs: carddav-txt-path-canonicalization.md (2 files)"]
  R3 --> V3["docs review"]
Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: security Security boundary, hardening, or vulnerability prevention bug Something isn't working priority: medium Normal-priority or P2 work status: blocked Blocked by conflict, dependency, or required prerequisite type: bug Defect or incorrect behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants