Skip to content

DO NOT MERGE: controlled Codex inline-review experiment#4

Draft
smakam wants to merge 4 commits into
mainfrom
codex/pr4-review-experiment
Draft

DO NOT MERGE: controlled Codex inline-review experiment#4
smakam wants to merge 4 commits into
mainfrom
codex/pr4-review-experiment

Conversation

@smakam

@smakam smakam commented Jul 17, 2026

Copy link
Copy Markdown
Owner

DO NOT MERGE

This draft pull request is a controlled fixture for comparing:

  1. Hosted Codex review triggered with @codex review.
  2. The repository-owned Action triggered with the codex-action-review label.

Both reviewers must evaluate the frozen head commit:

f2bc2a0

Do not push fixes, merge, or reveal expected findings until both reviews have completed.

Experiment procedure

  1. Attach the codex-action-review label.
  2. Comment @codex review.
  3. Wait for both reviews to complete on commit f2bc2a0.
  4. Compare findings, evidence, severity, noise, latency, and presentation.
  5. Close this pull request without merging after the experiment.

Local validation

  • Backend: 14 tests passed and 1 experiment-related test failed.
  • Repository baseline Ruff and ESLint configuration findings remain unchanged.

Summary by CodeRabbit

  • Bug Fixes
    • Corrected nameboard access control logic so authorization is evaluated against the correct session/user condition, reducing improper 403 access denials.
    • Fixed review correction handling so updates provided for medical registration numbers are applied to the correct qualifications information in extracted data.
    • Maintained consistent correction tracking, confidence updates, and review completion behavior after corrections are applied.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ee52ac1a-8f55-4a15-81a2-47103cfec4e3

📥 Commits

Reviewing files that changed from the base of the PR and between 77c4769 and 883b651.

📒 Files selected for processing (3)
  • .coderabbit.yaml
  • backend/app/routers/nameboard.py
  • backend/app/services/review_service.py

📝 Walkthrough

Walkthrough

The changes update nameboard access-denial logic, redirect medical registration corrections to qualifications, and add CodeRabbit review configuration. Endpoint routes, request/response shapes, and correction finalization behavior remain unchanged.

Changes

Access control

Layer / File(s) Summary
Access denial condition
backend/app/routers/nameboard.py
The non-uploader session comparison now raises 403 Access denied when the session ID equals the requesting user ID.

Correction mapping

Layer / File(s) Summary
Medical registration correction mapping
backend/app/services/review_service.py
Medical registration number corrections now target record.extracted_data["qualifications"].

Review configuration

Layer / File(s) Summary
CodeRabbit review settings
.coderabbit.yaml
Adds the review profile, security-focused path instructions, and code-guideline configuration.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 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 clearly reflects the PR’s main purpose as a controlled Codex review experiment.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ 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 codex/pr4-review-experiment

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

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

ℹ️ 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".


uploader = str(record.uploaded_by_customer_id) if record.uploaded_by_customer_id else None
if uploader != requesting_user_id and record.session_id != requesting_user_id:
if uploader != requesting_user_id and record.session_id == requesting_user_id:

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 Restore denial for non-owning users

When the requester is not the uploader and their session id is different from the record's session id, this predicate is false and _check_access lets the request continue for GET, accept, reject, and correct; conversely, the actual guest owner now gets a 403 because their session id matches. This breaks the repository security invariant that users must never access another user's extraction record; change this comparison back to denying when record.session_id != requesting_user_id.

Useful? React with 👍 / 👎.

Comment thread backend/app/services/result_service.py Outdated
pii = None
uploader_id = str(record.uploaded_by_customer_id) if record.uploaded_by_customer_id else None
if requesting_user_id and (uploader_id == requesting_user_id or record.session_id == requesting_user_id):
if requesting_user_id and (uploader_id == requesting_user_id or record.session_id != requesting_user_id):

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 Only decrypt PII for the matching session

For a non-owner record whose session_id differs from requesting_user_id, this condition now decrypts and returns pii_data instead of hiding it, which is the opposite of the uploader-only PII contract and the existing test_build_response_hides_pii_for_wrong_user expectation. Use record.session_id == requesting_user_id so PII is returned only to the uploader or owning guest session.

Useful? React with 👍 / 👎.

"specialisation": "specialisation",
"qualifications": "qualifications",
"medical_registration_no": "medical_registration_no",
"medical_registration_no": "qualifications",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Store registration corrections on the registration field

When the review page sends a changed medical_registration_no, this map writes the value into extracted_data.qualifications, overwriting the user's qualifications and leaving medical_registration_no unchanged for the saved response/audit record. Map this correction to medical_registration_no so the registration number the user reviewed is what gets persisted.

Useful? React with 👍 / 👎.

@smakam smakam added the codex-action-review trigger code review label Jul 17, 2026

@github-actions github-actions 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 Action review

Three actionable regressions: authorization is inverted, PII disclosure is inverted, and registration-number corrections overwrite qualifications.

Validation

  • Read AGENTS.md, ai/DECISIONS.md, and .github/review/code-review.md.
  • Reviewed the complete first-parent diff with relevant router, authentication, service, schema, and test context.
  • Attempted pytest -q from backend; validation could not run because pytest is not installed (exit 127).

Coverage

  • All three changed backend lines and their reachable API paths.
  • Uploader/session authorization and PII response behavior.
  • Correction field mapping and persistence behavior.
  • Existing result-service test expectations.

Uncertainty

  • Runtime tests were unavailable in the checkout environment because pytest is not installed.


uploader = str(record.uploaded_by_customer_id) if record.uploaded_by_customer_id else None
if uploader != requesting_user_id and record.session_id != requesting_user_id:
if uploader != requesting_user_id and record.session_id == requesting_user_id:

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 · high confidence — Inverted access check permits cross-user reads and mutations

Any authenticated user or guest with an ID different from a record's uploader/session can fetch, accept, reject, or correct that record by knowing its extraction ID. Conversely, a guest matching the record session is denied access to their own record.

Evidence: The condition raises 403 only when the uploader differs and record.session_id == requesting_user_id. All four ID-based endpoints call this check before reading or mutating the record, so a nonmatching attacker passes it.

Smallest correction: Restore the mismatch check: deny when both uploader != requesting_user_id and record.session_id != requesting_user_id.

Comment thread backend/app/services/result_service.py Outdated
pii = None
uploader_id = str(record.uploaded_by_customer_id) if record.uploaded_by_customer_id else None
if requesting_user_id and (uploader_id == requesting_user_id or record.session_id == requesting_user_id):
if requesting_user_id and (uploader_id == requesting_user_id or record.session_id != requesting_user_id):

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 · high confidence — PII condition decrypts data for the wrong user

A requester whose ID differs from the record session receives decrypted phone numbers and email addresses, while the matching guest session does not. Combined with the changed route access check, another user can retrieve a victim's encrypted-at-rest PII through the API.

Evidence: The new record.session_id != requesting_user_id branch makes the disclosure condition true for unrelated requesters. The existing test_build_response_hides_pii_for_wrong_user explicitly expects pii_data to be None for this case.

Smallest correction: Change the session comparison back to equality so PII is decrypted only when the requester matches the uploader UUID or session ID.

"specialisation": "specialisation",
"qualifications": "qualifications",
"medical_registration_no": "medical_registration_no",
"medical_registration_no": "qualifications",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 · high confidence — Registration corrections overwrite qualifications

Submitting only a corrected medical registration number updates the qualifications field, preserves the old qualification as its audit original, and leaves medical_registration_no unchanged. The accepted result therefore contains incorrect user-confirmed data.

Evidence: medical_registration_no now maps to the qualifications storage key, and the correction loop writes the submitted value to that mapped key with confidence 1.0.

Smallest correction: Map medical_registration_no to medical_registration_no.

@github-actions github-actions 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 Action review

Two actionable regressions found: broken extraction authorization and misrouted registration-number corrections.

Validation

  • Read AGENTS.md, .github/review/code-review.md, and ai/DECISIONS.md.
  • Inspected the complete first-parent diff and relevant router, authentication, schema, result-service, and test code.
  • git diff --check HEAD^1 HEAD passed.
  • pytest and python -m pytest -q could not run because pytest is not installed.

Coverage

  • Reviewed both changed backend files and all routes reaching the changed authorization helper.
  • Traced record ownership/session persistence, PII response gating, correction schemas, and extracted-data construction.
  • Reviewed existing relevant backend tests; they cover missing authentication and PII response filtering but not cross-user endpoint authorization or field correction mapping.

Uncertainty

  • The test suite could not be executed in the provided environment because pytest is unavailable.


uploader = str(record.uploaded_by_customer_id) if record.uploaded_by_customer_id else None
if uploader != requesting_user_id and record.session_id != requesting_user_id:
if uploader != requesting_user_id and record.session_id == requesting_user_id:

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 · high confidence — Restore fail-closed ownership check

Any authenticated user or guest session can read, accept, reject, or correct another user's extraction when its ID is known. The new condition rejects only the contradictory case where the requester is not the uploader but does match the stored session ID.

Evidence: All four record endpoints call _check_access. assemble_and_save stores the owner's ID in session_id, while the first-parent condition correctly denied access when both uploader and session differed. The PR changed the second comparison from != to ==.

Smallest correction: Restore if uploader != requesting_user_id and record.session_id != requesting_user_id: and add owner/non-owner tests for every affected endpoint.

"specialisation": "specialisation",
"qualifications": "qualifications",
"medical_registration_no": "medical_registration_no",
"medical_registration_no": "qualifications",

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 · high confidence — Do not overwrite qualifications with registration corrections

Submitting medical_registration_no updates the qualifications field instead, destroying or replacing qualification data while leaving the registration number unchanged.

Evidence: CorrectRequest and ExtractedData define distinct qualifications and medical_registration_no fields. The changed map now directs both correction attributes to qualifications; the first parent mapped the registration correction to its matching field.

Smallest correction: Map medical_registration_no to medical_registration_no and add a correction test asserting qualifications remain unchanged.

@smakam

smakam commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@smakam

smakam commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@smakam

smakam commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@github-actions github-actions 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 Action review

The pull request introduces an authorization bypass and corrupts medical-registration corrections.

Validation

  • Inspected the complete diff against HEAD^1 and relevant router, authentication, schema, result-service, review-service, and test code.
  • Attempted pytest -q in backend; validation could not run because pytest is not installed.

Coverage

  • Reviewed all three changed files: CodeRabbit configuration, nameboard authorization, and correction persistence.
  • Inspected unchanged authentication resolution, ownership persistence/PII response gating, request/response schemas, and existing access-control tests.

Uncertainty

  • Runtime tests were unavailable in the checkout environment; findings are established directly from reachable control flow and field definitions.


uploader = str(record.uploaded_by_customer_id) if record.uploaded_by_customer_id else None
if uploader != requesting_user_id and record.session_id != requesting_user_id:
if uploader != requesting_user_id and record.session_id == requesting_user_id:

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 · high confidence — Restore fail-closed ownership validation

Any authenticated or guest identity whose ID differs from both ownership fields can read, accept, reject, or correct another user's extraction by knowing its ID. Conversely, the session owner is denied when the uploader field differs.

Evidence: The condition rejects only when uploader != requesting_user_id and record.session_id == requesting_user_id. Thus a non-owner with a different session ID bypasses _check_access. All four record endpoints call this check before returning data or mutating the record.

Smallest correction: Change the session comparison back to record.session_id != requesting_user_id, rejecting unless either ownership field matches.

"specialisation": "specialisation",
"qualifications": "qualifications",
"medical_registration_no": "medical_registration_no",
"medical_registration_no": "qualifications",

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 · high confidence — Map registration corrections to the registration field

Submitting medical_registration_no overwrites the doctor's qualifications while leaving the registration number unchanged. If both corrections are submitted, the registration number replaces the qualifications because it is processed later.

Evidence: CorrectRequest and persisted ExtractedData have separate qualifications and medical_registration_no fields, but the changed mapping sends both request attributes to qualifications.

Smallest correction: Map medical_registration_no to medical_registration_no.

@smakam

smakam commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@github-actions github-actions 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 Action review

Two actionable regressions found: broken record authorization and incorrect correction-field mapping.

Validation

  • git diff --check HEAD^1 HEAD passed.
  • pytest -q could not run because pytest is not installed in the checkout environment.

Coverage

  • Reviewed the complete first-parent diff: CodeRabbit configuration, nameboard router authorization, and review-service correction mapping.
  • Inspected authentication resolution, record persistence/session ownership, response PII gating, schemas, and existing backend tests.

Uncertainty

  • No pull-request description beyond the merge commit message was available.
  • Automated tests were unavailable because the existing environment lacks pytest.


uploader = str(record.uploaded_by_customer_id) if record.uploaded_by_customer_id else None
if uploader != requesting_user_id and record.session_id != requesting_user_id:
if uploader != requesting_user_id and record.session_id == requesting_user_id:

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 · high confidence — Restore fail-closed ownership checking

The condition now rejects a requester only when they are not the uploader but their ID equals the record session. Consequently, an authenticated user or guest possessing another extraction ID can read its extracted data and accept, reject, or correct it. Guest owners are also denied access to their own records.

Evidence: Records store the owner identity in uploaded_by_customer_id and/or session_id. For a non-owner, both comparisons are unequal, making the new condition false and allowing execution. For a guest owner, uploader is null and session_id == requesting_user_id, making it return 403.

Smallest correction: Restore if uploader != requesting_user_id and record.session_id != requesting_user_id:.

"specialisation": "specialisation",
"qualifications": "qualifications",
"medical_registration_no": "medical_registration_no",
"medical_registration_no": "qualifications",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 · high confidence — Write registration corrections to the registration field

Submitting medical_registration_no overwrites qualifications, while the registration number remains unchanged. If both fields are submitted, the registration correction also replaces the qualifications correction because it is processed later.

Evidence: CorrectRequest and ExtractedData define distinct medical_registration_no and qualifications fields, but the changed map routes both request attributes to the qualifications key.

Smallest correction: Map medical_registration_no to medical_registration_no.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@smakam

smakam commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai why did you not flag the access-control and medical_registration_no mapping changes as actionable issues?

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Relevant file outlines =='
ast-grep outline backend/app/routers/nameboard.py --items all --view expanded 2>/dev/null || true
ast-grep outline backend/app/services/review_service.py --items all --view expanded 2>/dev/null || true

echo '== Configuration =='
cat .coderabbit.yaml 2>/dev/null || true
echo
fd -a '^code-review\.md$' .github 2>/dev/null | while IFS= read -r f; do
  echo "== $f =="; cat "$f"; echo
done

echo '== Diff from stated frozen commit f2bc2a0 =='
git diff --find-renames --unified=40 f2bc2a0..HEAD -- \
  backend/app/routers/nameboard.py \
  backend/app/services/review_service.py \
  .coderabbit.yaml \
  .github/review/code-review.md || true

echo '== Access-control implementation and tests =='
rg -n -C 16 --glob '*.py' \
  '(def _check_access\b|_check_access\s*\(|session_id|Access denied|NameboardController)' \
  backend 2>/dev/null || true

echo '== Correction mapping implementation and tests =='
rg -n -C 16 --glob '*.py' \
  '(def correct\b|medical_registration_no|qualifications|CORRECTABLE_FIELDS|correction)' \
  backend 2>/dev/null || true

echo '== PR review metadata and comments =='
gh pr view 4 --repo smakam/doctor-parser --json reviews,comments,reviewThreads 2>/dev/null || true
gh api repos/smakam/doctor-parser/pulls/4/comments --paginate 2>/dev/null || true

Length of output: 115703


@smakam, it should have flagged both. This was a review false negative, not an ambiguity in the code or an inadequacy of the instructions.

  • Access control — P1: backend/app/routers/nameboard.py Line 154 changed the deny condition from “neither ownership identifier matches” to “the session identifier matches.” A requester whose ID differs from both the uploader and session IDs now passes _check_access and can retrieve, accept, reject, or correct another record by ID; a matching guest session is denied instead.
  • Registration correction mapping — P2: backend/app/services/review_service.py Line 46 maps medical_registration_no to qualifications. Submitting that correction overwrites qualifications, leaves the registration number unchanged, and can overwrite a qualifications correction submitted in the same request.

The configured guidance was already explicit and correct: .coderabbit.yaml calls out both ownership/session validation and logical-field-to-persisted-field mapping, while .github/review/code-review.md makes authorization the highest priority. So this is not a case where the findings were filtered out as non-actionable.

The PR’s published inline comments confirm the contrast: hosted Codex and the repository-owned Action each reported both defects, whereas the CodeRabbit full-review invocations completed without CodeRabbit-authored inline findings. I can verify that outcome, but the available PR metadata does not expose an internal model decision or suppression reason that would justify a more specific root-cause claim.

The appropriate expected output from CodeRabbit was therefore two actionable findings—P1 for the authorization bypass and P2 for the incorrect correction mapping. Repeating a full review did not correct that miss, despite the relevant instructions being present.

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

Labels

codex-action-review trigger code review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant