Skip to content

fix: exit non-zero on an error result in acf_fields and jetengine_fields (3.9.1) - #18

Merged
BenKalsky merged 2 commits into
mainfrom
fix/error-exit-codes
Sep 12, 2026
Merged

BenKalsky merged 2 commits into
mainfrom
fix/error-exit-codes

Conversation

@BenKalsky

Copy link
Copy Markdown
Member

Two follow-ups from #17, both of which I had left open there.

1. The exit-code bug was in three scripts, not one

acf_fields, jetengine_fields and seo_meta all report a failed write as {"error": ...} rather than by raising, so the CLI printed the error and still exited 0 — which CI and an agent both read as success. #17 fixed seo_meta and I listed the other two under "not in scope" because they were not in the audit.

That was the wrong call. It is the same bug, in scripts that sit beside the one I fixed, and "not in the audit" is not a reason to ship a known failure. Both are fixed here.

The check is now one shared security.exit_on_error_result instead of three copies, with a static test that fails if a script returning an error dict does not call it. I verified that test fails with the call removed, so it is not vacuous.

Nothing changes for a successful call, and the JSON still goes to stdout — only the exit code differs.

2. The requests claim is now proven rather than asserted

#17 narrowed the credential-leak finding from the audit's 13 scripts to 9, on the grounds that requests strips Authorization across hosts by itself. I could not run that locally then — requests was not installed — and said so rather than claiming it.

Now measured, against requests 2.32.5 in a throwaway venv, with two local servers:

cross-host redirect:
  origin saw auth       : Basic dXNlcjpTRUNSRVQ=
  cross-host target saw : None          → STRIPPED

same-origin redirect (302 to a new path on the same host and port):
  first hop saw : Basic dXNlcjpTRUNSRVQ=
  same-origin   : Basic dXNlcjpTRUNSRVQ=  → KEPT

Same semantics as the urllib opener #17 added, in both directions. The four requests-based scripts need no equivalent fix, and the narrowing in #17 was correct.

One detail worth recording: my first same-origin control was mis-designed — it used two servers, so the redirect crossed a port, and requests strips there too (its should_strip_auth treats a port change as origin-crossing, exactly as same_origin does). The control above redirects within one server.

Tests: 80 → 85.

🤖 Generated with Claude Code

…r (3.9.1)

Both print {"error": ...} and returned 0 on a failed write, so CI and an agent
both read a failure as success. 3.9.0 fixed exactly this in seo_meta and I
flagged the other two as out of scope rather than fixing them; they are the
same bug and the scope argument does not survive a second look.

The check is now one shared security.exit_on_error_result rather than three
copies, with a static test that fails if a script returning an error dict does
not call it - verified to fail with the call removed, so it is not vacuous.

Also recorded in the changelog: requests 2.32.5 was verified empirically to
strip Authorization on a cross-host redirect and keep it on a same-origin one,
the same semantics as 3.9.0's urllib opener. 3.9.0 asserted that from library
documentation without being able to run it; it is now proven, and the four
scripts that authenticate through requests need no equivalent fix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@BenKalsky

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 12, 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-12T19:43:10.643264Z f22d8b4 Manual request
ℹ️ 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: 92755623b0

ℹ️ 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 wordpress-api-pro/scripts/security.py Outdated
Comment on lines +385 to +386
if isinstance(result, dict) and result.get("error"):
sys.exit(1)

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 Distinguish field data from error envelopes

When an ACF or JetEngine read succeeds and the returned custom fields include a truthy field named error (including an explicit --field error lookup), the result is ordinary user data but this generic check exits with status 1. Both getters return their field dictionaries directly, so key presence cannot reliably identify a helper failure; use a distinguishable error result or otherwise keep successful field data from reaching this predicate.

Useful? React with 👍 / 👎.

Sniffing for an "error" key cannot identify a failure in these scripts: the
ACF and JetEngine getters return the SITE's own field dictionary, so a custom
field named "error" - or an explicit "--field error" lookup - is ordinary data
that my check would have turned into exit 1 on a perfectly successful read.

Failure is now a type. security.ErrorResult is a dict subclass built by
error_result(), so the JSON output is byte-identical and callers can still
index it, but exit_on_error_result tests isinstance rather than guessing from
content. All thirteen bare {"error": ...} returns across acf_fields,
jetengine_fields and seo_meta go through it.

Two static tests hold the line: no script may build a bare {"error": ...}
return, and any script that builds a failure must check it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@BenKalsky

Copy link
Copy Markdown
Member Author

Fixed in f22d8b4, and this one was a design flaw rather than a missed call site - you are right that key presence cannot identify a failure here.

The ACF and JetEngine getters return the site's own field dictionary. A WordPress custom field named error with any truthy value, or an explicit --field error lookup, would have exited 1 on a perfectly successful read. My check invented a failure out of a customer's data.

Failure is now carried by a type. security.ErrorResult is a dict subclass built by error_result(), so the JSON output is byte-identical and callers can still index it, but exit_on_error_result tests isinstance rather than guessing from content. All thirteen bare {"error": ...} returns across the three scripts go through it.

Demonstrated:

site data with an error field: exit 0 OK
genuine failure: exit 1
json identical: {"error": "boom"}

Two static tests hold the line: no script may build a bare {"error": ...} return, and any script that builds a failure must check it.

87 tests passing.

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: f22d8b4910

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

@BenKalsky
BenKalsky merged commit 0015ee2 into main Sep 12, 2026
3 checks passed
@BenKalsky
BenKalsky deleted the fix/error-exit-codes branch September 12, 2026 19:48
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