Skip to content

Audit remediation: security hardening, trust docs, discoverability (v0.0.5) - #10

Merged
robotlearning123 merged 5 commits into
mainfrom
harden/audit-2026-06-18
Jun 18, 2026
Merged

Audit remediation: security hardening, trust docs, discoverability (v0.0.5)#10
robotlearning123 merged 5 commits into
mainfrom
harden/audit-2026-06-18

Conversation

@robotlearning123

@robotlearning123 robotlearning123 commented Jun 18, 2026

Copy link
Copy Markdown
Owner

Full remediation of the 2026-06-18 cross-model audit (cx + cx2 + 4× ccz waves, Opus-verified). Stacked on PR #9 — includes the heavy-DR widget fix (2807473) plus the hardening below. Reviewer: cx (GPT-5.5) ran a final pass on the complete diff → clean after two follow-up fixes (now included).

Multi-concern PR by design (the goal was "fix all"). Happy to split into security / docs / hygiene PRs if preferred. Supersedes #9.

Security

  • Default bind 127.0.0.1 (was 0.0.0.0). The HTTP transport is unauthenticated and proxies a full ChatGPT account; the server now refuses non-loopback HTTP unless GPT2AGENT_ALLOW_REMOTE=1, with a loud warning. config.example.toml + gpt2agent setup now generate loopback. stdio (the install default) is unaffected.
  • Broader secret redactionredact_error now also strips bare Bearer, named JSON token fields, auth/session cookies, and token query params; backend.py + streaming SSE error paths route through it (were raw).
  • Removed internal agent-orchestration notes (docs/goals/*.md) from the repo.

Correctness

  • Heavy-DR widget parser hardening — requires author.role ∈ {tool,assistant}, prefix must start the part, and only a finished_successfully report is emitted → closes a DR-report-spoofing vector and premature-done on in-progress drafts.
  • stream() typed AsyncIterator[str | dict]; agent/chat/gpt_chat/memory return "(no response)" on timeout (not "").
  • CODEX_HOME honored in auth.py/setup.py.
  • install.sh falls back to git+https on PyPI 404 → the one-liner works pre-publish.

Docs / trust / discoverability

  • README Security & risk section (ToS/account-ban, unauthenticated HTTP, redaction limits); honest "(emails/phones redacted)" wording; reconciled the PyPI auto-publish claim with the required one-time Trusted Publisher setup.
  • Added SECURITY.md, issue templates, PR template. Bumped to 0.0.5 + CHANGELOG. GitHub topics + description updated.

⚠️ Owner action still required (cannot be done in code)

Configure a PyPI Trusted Publisher (project gpt2agent, owner robotlearning123, workflow release.yml, env pypi) or do a first manual twine upload. Until then every Release run fails with invalid-publisher and pip install gpt2agent 404s (the installer git-fallback mitigates for users).

Verification

  • ruff check clean · pytest -q61 passed, 9 skipped (+15 hardening tests in tests/test_security_hardening.py).
  • cx final review verdict + per-finding receipts retained locally.

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes v0.0.5

  • Security

    • Default HTTP bind now restricted to loopback (127.0.0.1) for unauthenticated transport
    • Enhanced secret redaction for bearer tokens, auth cookies, and query parameters in logs
    • Added SECURITY.md for vulnerability reporting guidance
  • Bug Fixes

    • Fixed Deep Research report extraction from connector state
    • Fixed timeout handling to display "(no response)" instead of empty results
    • Fixed CODEX_HOME environment variable support
    • Fixed installation fallback for PyPI failures
  • Documentation

    • Added GitHub issue and pull request templates
    • Enhanced README with security warnings and configuration details

robotlearning123 and others added 5 commits June 11, 2026 14:56
ChatGPT moved heavy Deep Research to the "Deep Research App" connector
(connectors://connector_openai_deep_research), which renders the report in an
embedded widget and never writes it as an assistant text node. The old
_poll_dr_completion scanned only assistant text, so heavy runs timed out at
1800s with an empty report even though the research completed server-side.

The report lives in the hidden widget state (widget_state.report_message).
_poll_dr_completion now fetches the conversation with
?include_visually_hidden_messages=true&include_widget_state=true and recovers
the report (text + content_references) from either widget-state carrier — a
"The latest state of the widget is: {…}" tool node, or
message.metadata.chatgpt_sdk.widget_state — via the new
_dr_report_from_widget_state helper. Existing citation-extraction is untouched.

Verified by recovering three real completed reports headlessly
(45.6K / 52.4K / 51.5K chars, with citations). Adds 4 fixture-based tests
(real oracle, no network). Light deep_research uses a different (SearchGPT)
mechanism and is unchanged; a dedicated light-mode fix is tracked as a follow-up.

Bumps version 0.0.3 -> 0.0.4; updates deep-research skill doc + CHANGELOG.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… parser

Security:
- Default bind to 127.0.0.1; refuse non-loopback HTTP unless GPT2AGENT_ALLOW_REMOTE=1
  (the HTTP transport is unauthenticated and proxies a full ChatGPT account).
  config.example.toml now ships loopback. (_http_bind_decision helper, unit-tested)
- Route backend.py + streaming SSE error bodies through redact_error.
- Broaden _log_redact: bare Bearer, access_token/session_token JSON fields,
  auth/session cookies, token query params (was: quoted header pairs only).

Correctness (DR widget recovery, PR #9 follow-up):
- _dr_report_from_widget_state now requires author.role in (tool,assistant),
  the prefix to START the part (not substring), and a finished report status —
  blocks DR-report spoofing via a user message and premature in-progress emission.
- stream() typed AsyncIterator[str | dict]; complete() skips non-str sentinels.
- agent/chat/gpt_chat/memory tools return "(no response)" instead of "" on timeout.

Lint: drop dead `done` var + unused `groups`; fix E402 import order; assert on
live /backend-api/me result.

Tests: +14 (tests/test_security_hardening.py) for redaction, bind gate, parser
guards, and the widget-state query-flag assertion. Full suite 60 passed, 9 skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019UxBmhmJvjdh3qA9FKBNZR
…staller

- Remove docs/goals/*.md internal agent-orchestration briefings from the public
  repo (leaked writer/reviewer roles + DR quota usage). NOTE: still in git
  history — a history purge would need a separate, explicit force-push.
- Honor CODEX_HOME in auth.py and setup.py (mirrors backend.py) so multi-account
  users read/save the intended login, not always ~/.codex.
- install.sh: on PyPI install failure, auto-fall back to
  git+https://github.com/robotlearning123/gpt2agent.git instead of exiting — the
  one-line install works today (pre-PyPI-publish) and switches to PyPI seamlessly
  once the package is published.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019UxBmhmJvjdh3qA9FKBNZR
…le claims; 0.0.5

- README: add a prominent "Security & risk" section (ToS/account-ban from web-client
  impersonation, unauthenticated HTTP transport + loopback default, limited PII
  redaction, RAW_DUMP warning). Honest "(emails/phones redacted)" wording.
  Reconcile the "auto-publish to PyPI" claim with the required one-time Trusted
  Publisher setup; default config example to 127.0.0.1; de-stale version refs.
- Add SECURITY.md (private reporting + documented risk model), issue templates
  (bug/feature + security contact link), and a PR template.
- CLAUDE.md test count 38 -> 60.
- Bump to 0.0.5 with CHANGELOG covering the security/correctness hardening.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019UxBmhmJvjdh3qA9FKBNZR
- setup.py: generated config now writes host = "127.0.0.1" (was 0.0.0.0). With the
  new bind gate, `gpt2agent setup` had been self-generating a config it would then
  refuse to start. (cx P1)
- sse.py: guard `chatgpt_sdk` with isinstance(dict) before .get() — a non-dict
  scalar carrier no longer aborts _dr_report_from_widget_state / DR polling. (cx P2)
- test: regression for the malformed-sdk carrier.

cx final review (writer=Opus, reviewer=cx GPT-5.5): otherwise clean — bind gate
conservative + stdio bypasses it, redaction bounded to error strings (not response
JSON), legitimate completed widget fixtures still pass. Suite: 61 passed, 9 skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019UxBmhmJvjdh3qA9FKBNZR
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Release 0.0.5 adds loopback-only HTTP bind enforcement with an opt-in env var, expands the secret-redaction pipeline to cover bearer tokens, cookies, and query params, adds CODEX_HOME-aware token discovery, recovers heavy Deep Research reports from connector widget state during polling, normalizes empty tool responses to "(no response)", and adds SECURITY.md, GitHub community templates, and updated docs.

Changes

gpt2agent v0.0.5: Security Hardening and Deep Research Widget-State Recovery

Layer / File(s) Summary
Expanded secret redaction pipeline
gpt2agent/_log_redact.py, gpt2agent/backend.py, gpt2agent/sentinel.py, gpt2agent/sse.py
_log_redact.py adds regex patterns for JSON token fields, bare Bearer strings, auth/session cookies, and token query params; redact_error applies them sequentially before truncation. backend.py DR error paths and sse.py DR HTTP errors use redact_error instead of raw sliced text; sentinel.py moves its import earlier.
Loopback-only HTTP transport enforcement
gpt2agent/server.py, gpt2agent/setup.py, config.example.toml
server.py changes default host to 127.0.0.1, introduces _http_bind_decision() returning ok-loopback/ok-remote/refuse, and main() blocks streamable-http on non-loopback unless GPT2AGENT_ALLOW_REMOTE=1. Generated config.toml and config.example.toml follow suit.
CODEX_HOME-aware token discovery
gpt2agent/auth.py, gpt2agent/setup.py
auth.py and setup.py derive the Codex auth.json path from $CODEX_HOME when set, falling back to ~/.codex/auth.json.
Heavy DR connector widget-state parsing
gpt2agent/sse.py
Introduces _WIDGET_STATE_TEXT_PREFIX, _coerce_widget_state(), and _dr_report_from_widget_state() with anti-spoofing prefix and finished/completed status gating. stream() return type extended to AsyncIterator[str | dict]; complete() separates dict sentinel events. _poll_dr_completion adds include_visually_hidden_messages and include_widget_state query flags and emits done immediately when widget text is recovered.
Widget-state fixture and tests
tests/fixtures/heavy_dr_widget_state.json, tests/test_heavy_dr_parser.py, tests/test_security_hardening.py
Fixture provides three widget-state carriers. Tests exercise _dr_report_from_widget_state extraction for both carrier types and the empty case, poll-completion done event recovery, anti-spoofing/role-gating/malformed-metadata hardening, and query-flag presence in the widget-state fetch.
Redaction and bind-decision unit tests
tests/test_security_hardening.py
Offline tests verify redact_error handles bearer, named fields, session cookies, auth headers, non-secret param preservation, and truncation; _http_bind_decision accepts loopback, refuses non-loopback by default, and allows with opt-in.
Server tool response normalization and DR groups cleanup
gpt2agent/server.py
chat, agent, gpt_chat, and memory_create_via_chat return "(no response)" for empty conv.complete() output. deep_research drops search_result_groups accumulation; deep_research_heavy removes unused groups variable.
Deep Research skill docs and docstring updates
gpt2agent/skills/deep-research/SKILL.md, gpt2agent/skills/deep-research/bin/deep_research.py
SKILL.md bumped to 0.1.1, --heavy usage updated to describe widget-state recovery, Known Limitation replaced with widget-state retrieval section. Script docstring and citation fallback note updated.
Security policy, README hardening, community files
SECURITY.md, README.md, .github/ISSUE_TEMPLATE/*, .github/pull_request_template.md
SECURITY.md created with private-advisory reporting, threat model, and scope. README.md Security & risk section rewritten with explicit HTTP transport warnings, GPT2AGENT_ALLOW_REMOTE, GPT2AGENT_RAW_DUMP, and SECURITY.md link. Bug report, feature request, and PR templates added; blank issues disabled.
Version bump, installer fallback, changelog, metadata
pyproject.toml, install.sh, CHANGELOG.md, CLAUDE.md, README.md
Version bumped 0.0.30.0.5. install.sh now falls back to git+https before exiting on PyPI failure. CHANGELOG.md adds 0.0.5 section. CLAUDE.md test count updated. README.md Release section rewritten. Internal docs/goals/heavy-dr-*.md notes removed.

Sequence Diagram(s)

sequenceDiagram
    participant MCP_Client
    participant server_main
    participant _http_bind_decision
    participant ConversationClient
    participant _poll_dr_completion
    participant BackendGet

    MCP_Client->>server_main: start streamable-http
    server_main->>_http_bind_decision: check host + GPT2AGENT_ALLOW_REMOTE
    alt non-loopback, no opt-in
        _http_bind_decision-->>server_main: refuse
        server_main-->>MCP_Client: SystemExit
    else loopback or opt-in
        _http_bind_decision-->>server_main: ok
        server_main-->>MCP_Client: server started

        MCP_Client->>ConversationClient: deep_research_heavy(prompt)
        ConversationClient->>_poll_dr_completion: poll for completion
        loop poll cycle
            _poll_dr_completion->>BackendGet: GET ?include_visually_hidden_messages=true&include_widget_state=true
            BackendGet-->>`_poll_dr_completion`: conversation JSON
            alt widget_state has finished report
                `_poll_dr_completion`->>ConversationClient: emit progress + done (early return)
            else no widget report yet
                `_poll_dr_completion`->>ConversationClient: emit progress, continue
            end
        end
        ConversationClient-->>MCP_Client: final done event with report + content_references
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • robotlearning123/gpt2agent#8: Directly extends the heavy-DR connector widget-state parsing, metadata/citation recovery, and overlapping CODEX_HOME token-handling work introduced in that PR.

Poem

🐇 Hoppity-hop, the secrets are sealed,
No bearer tokens left unconcealed!
The loopback guard stands firm and tight,
Widget-state reports now stream just right. 🌟
From 127.0.0.1 we bind with care,
A safer agent hops through the air! 🔒

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 39.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main changes: security hardening, trust documentation, and discoverability improvements for version 0.0.5.
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
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch harden/audit-2026-06-18

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 and usage tips.

@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: 2317307cc0

ℹ️ 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 gpt2agent/_log_redact.py
Comment on lines +26 to +28
_TOKEN_FIELD_RE = re.compile(
r'"((?:access|session|id|refresh|bearer)[_-]?token|accessToken)"\s*:\s*"[^"]*"',
re.IGNORECASE,

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 Redact Python-repr token fields

When callers pass a Python dict representation (for example sentinel.get_tokens() uses _redact_error(str(resp)) on the no-token branch), this new pattern does not match single-quoted keys, so {'tokens': {'access_token': 'SECRET'}} is returned with SECRET intact. That leaves the redaction hardening incomplete for an existing error path; handle single-quoted reprs too or serialize dicts to JSON before redacting.

Useful? React with 👍 / 👎.

Comment thread gpt2agent/sse.py
Comment on lines +1577 to +1578
if widget_text != last_emitted:
yield {"type": "progress", "text": widget_text}

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 Emit only widget report deltas

When the SSE phase has already yielded partial report text and then falls back to polling, last_emitted is initialized from that seed text, but this branch emits the entire widget_text as a progress event whenever it differs. Direct consumers that concatenate progress chunks, matching the delta semantics used by the assistant-text polling path below, will see seed_text + widget_text and duplicate the prefix of the final report; emit only the suffix when widget_text.startswith(last_emitted).

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@gpt2agent/setup.py`:
- Around line 43-44: The CODEX_HOME environment variable path is not being
expanded, which means literal tilde characters in the path are not being
converted to the user's home directory. In setup.py where codex_home is
retrieved from the environment and used to construct the Path object (lines
43-44), apply expanduser() to the codex_home string before converting it to a
Path to properly resolve any tilde characters. Apply this same fix consistently
to gpt2agent/auth.py at lines 24-25 where a similar CODEX_HOME path resolution
pattern exists.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e24ae320-6dd7-4b05-ab47-e667bc803373

📥 Commits

Reviewing files that changed from the base of the PR and between c90116c and 2317307.

📒 Files selected for processing (26)
  • .github/ISSUE_TEMPLATE/bug_report.md
  • .github/ISSUE_TEMPLATE/config.yml
  • .github/ISSUE_TEMPLATE/feature_request.md
  • .github/pull_request_template.md
  • CHANGELOG.md
  • CLAUDE.md
  • README.md
  • SECURITY.md
  • config.example.toml
  • docs/goals/heavy-dr-citations-RESULT.md
  • docs/goals/heavy-dr-citations.md
  • gpt2agent/_log_redact.py
  • gpt2agent/auth.py
  • gpt2agent/backend.py
  • gpt2agent/sentinel.py
  • gpt2agent/server.py
  • gpt2agent/setup.py
  • gpt2agent/skills/deep-research/SKILL.md
  • gpt2agent/skills/deep-research/bin/deep_research.py
  • gpt2agent/sse.py
  • install.sh
  • pyproject.toml
  • tests/fixtures/heavy_dr_widget_state.json
  • tests/test_backend_tools.py
  • tests/test_heavy_dr_parser.py
  • tests/test_security_hardening.py
💤 Files with no reviewable changes (2)
  • docs/goals/heavy-dr-citations-RESULT.md
  • docs/goals/heavy-dr-citations.md

Comment thread gpt2agent/setup.py
Comment on lines +43 to +44
codex_home = os.environ.get("CODEX_HOME")
p = (Path(codex_home) if codex_home else Path.home() / ".codex") / "auth.json"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify current CODEX_HOME path handling and pathlib expansion behavior.
rg -n 'Path\(codex_home\)|CODEX_HOME' gpt2agent/setup.py gpt2agent/auth.py

python - <<'PY'
from pathlib import Path
raw = Path("~/.codex-cx2") / "auth.json"
expanded = Path("~/.codex-cx2").expanduser() / "auth.json"
print("raw      :", raw)
print("expanded :", expanded)
PY

Repository: robotlearning123/gpt2agent

Length of output: 703


Expand CODEX_HOME with expanduser() before resolving auth.json.

Path(codex_home) keeps a literal ~ in common env-file/quoted setups, so token lookup can miss a valid Codex login path.

Suggested fix
-    codex_home = os.environ.get("CODEX_HOME")
-    p = (Path(codex_home) if codex_home else Path.home() / ".codex") / "auth.json"
+    codex_home = os.environ.get("CODEX_HOME")
+    codex_base = (
+        Path(codex_home).expanduser() if codex_home else Path.home() / ".codex"
+    )
+    p = codex_base / "auth.json"

Apply the same fix to gpt2agent/auth.py (lines 24–25) for consistency.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
codex_home = os.environ.get("CODEX_HOME")
p = (Path(codex_home) if codex_home else Path.home() / ".codex") / "auth.json"
codex_home = os.environ.get("CODEX_HOME")
codex_base = (
Path(codex_home).expanduser() if codex_home else Path.home() / ".codex"
)
p = codex_base / "auth.json"
🤖 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 `@gpt2agent/setup.py` around lines 43 - 44, The CODEX_HOME environment variable
path is not being expanded, which means literal tilde characters in the path are
not being converted to the user's home directory. In setup.py where codex_home
is retrieved from the environment and used to construct the Path object (lines
43-44), apply expanduser() to the codex_home string before converting it to a
Path to properly resolve any tilde characters. Apply this same fix consistently
to gpt2agent/auth.py at lines 24-25 where a similar CODEX_HOME path resolution
pattern exists.

@robotlearning123
robotlearning123 merged commit 19512e9 into main Jun 18, 2026
10 checks passed
@robotlearning123
robotlearning123 deleted the harden/audit-2026-06-18 branch June 18, 2026 21:10
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