Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
3abcaad
fix(security): redact router credential diagnostics
seonghobae Aug 22, 2026
745df0b
Merge remote-tracking branch 'origin/main' into fix/agent-mention-rou…
seonghobae Aug 22, 2026
41798f0
Merge latest main into credential diagnostic branch
seonghobae Aug 22, 2026
cd91a09
Merge remote-tracking branch 'origin/main' into fix/agent-mention-rou…
seonghobae Aug 22, 2026
6e17c3f
Merge remote-tracking branch 'origin/main' into fix/agent-mention-rou…
seonghobae Aug 22, 2026
785401d
Merge remote-tracking branch 'origin/main' into codex/pr1215-restack
seonghobae Aug 23, 2026
ad028d9
Merge remote-tracking branch 'origin/main' into fix/agent-mention-rou…
claude Aug 30, 2026
6a4ffae
Merge branch 'main' into fix/agent-mention-router-redact-diagnostics
seonghobae Aug 30, 2026
8f75aad
Merge branch 'main' into fix/agent-mention-router-redact-diagnostics
seonghobae Aug 30, 2026
1be741a
Merge remote-tracking branch 'origin/main' into HEAD
seonghobae Sep 3, 2026
9d3aaf2
Merge branch 'main' into fix/agent-mention-router-redact-diagnostics
opencode-agent[bot] Sep 4, 2026
acdff45
Merge branch 'main' into fix/agent-mention-router-redact-diagnostics
opencode-agent[bot] Sep 4, 2026
2f0706f
Merge branch 'main' into fix/agent-mention-router-redact-diagnostics
opencode-agent[bot] Sep 4, 2026
2df7ace
Merge branch 'main' into fix/agent-mention-router-redact-diagnostics
opencode-agent[bot] Sep 5, 2026
ca69bab
Merge branch 'main' into fix/agent-mention-router-redact-diagnostics
opencode-agent[bot] Sep 5, 2026
e860d24
Merge branch 'main' into fix/agent-mention-router-redact-diagnostics
opencode-agent[bot] Sep 5, 2026
a7b2f1c
Merge branch 'main' into fix/agent-mention-router-redact-diagnostics
opencode-agent[bot] Sep 5, 2026
69d0028
Merge branch 'main' into fix/agent-mention-router-redact-diagnostics
opencode-agent[bot] Sep 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1326,6 +1326,8 @@ Semantic Versioning where the repository publishes a release.

### Fixed

- Redacted the exact agent-mention GitHub credential from bounded CLI failure
diagnostics while retaining exit status and actionable non-secret stderr.
- Prefer the job-scoped `github.token` when the central OpenCode dispatch
publishes a commit status back to the same `.github` repository. The job's
declared `statuses: write` permission now reaches the endpoint instead of an
Expand Down
16 changes: 16 additions & 0 deletions docs/doctoring/agent-mention-concurrency-isolation.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ whose pull-list request completes first can expose its recent PR comments
before a slower sibling. Dispatch remains sequential through the existing
ledger and exact-head validation boundaries.

### Credential-safe transport diagnostics

The router passes its GitHub credential only through `GH_TOKEN`, never through
the command arguments. GitHub CLI failures can still echo that credential in
standard error, so `GitHubClient.request` removes the client's exact token
before applying the existing 2,000-character diagnostic bound. Timeout errors
retain their fixed message and do not add command arguments. This follows the
OWASP logging guidance to remove access tokens while preserving actionable
operational evidence; it does not mask operational PII.

## Preserved boundaries

- No model provider, reviewer identity, repository allowlist, token name, credential scope, or branch-protection rule changes.
Expand All @@ -75,6 +85,8 @@ ledger and exact-head validation boundaries.
- Only trusted non-bot `OWNER`, `MEMBER`, or `COLLABORATOR` comments on open pull requests are eligible.
- Pull request number, exact head and base SHAs, base branch, source comment, requested agent, and requesting actor remain bound to the invocation key.
- Mention routing remains unable to approve, merge, update branches, publish, or release.
- GitHub CLI diagnostics retain exit status and sanitized error text but never
the active router credential.

## Operational acceptance

Expand All @@ -87,6 +99,8 @@ After protected integration:
5. distinguish downstream provider or review failure from routing failure rather than treating every missing verdict as the same incident.
6. verify the slow-first/fast-later repository regression remains green so a
delayed repository cannot starve a completed sibling's comment inventory.
7. force a GitHub CLI failure that echoes the synthetic client credential and
require `[REDACTED]` in the raised diagnostic with no credential value.

A receipt proves routing and durable claim processing. It is not an approval and never substitutes for exact-head checks or branch protection.

Expand All @@ -110,4 +124,6 @@ GitHub. (n.d.). *REST API endpoints for repositories: Create a repository dispat

GitHub. (n.d.). *Store and share data with workflow artifacts*. GitHub Docs. Retrieved August 19, 2026, from https://docs.github.com/en/actions/tutorials/store-and-share-data

OWASP Foundation. (n.d.). *Logging cheat sheet*. OWASP Cheat Sheet Series. Retrieved August 22, 2026, from https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html

Python Software Foundation. (n.d.). *concurrent.futures — Launching parallel tasks*. Python documentation. Retrieved August 20, 2026, from https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.as_completed
1 change: 1 addition & 0 deletions scripts/ci/agent_mention_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ def request(
diagnostic = " ".join(str(getattr(completed, "stderr", "") or "").split())
if not diagnostic:
diagnostic = "no stderr output"
diagnostic = diagnostic.replace(self._token, "[REDACTED]")
Comment thread
seonghobae marked this conversation as resolved.
retryable = RATE_LIMIT_DIAGNOSTIC_RE.search(diagnostic) is not None
if retryable and attempt < GITHUB_API_MAX_ATTEMPTS:
time.sleep(attempt * 5)
Expand Down
15 changes: 14 additions & 1 deletion tests/test_agent_mention_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,7 @@ def test_dispatch_noema_only_covers_non_opencode_path() -> None:


def test_github_client_validates_token_and_decodes_json(monkeypatch) -> None:
"""The token-bound client never places credentials in command arguments."""
"""The token-bound client keeps credentials out of commands and errors."""

module = load_module()
with pytest.raises(ValueError, match="token"):
Expand All @@ -712,6 +712,19 @@ def fake_run(command, **kwargs):
lambda *args, **kwargs: SimpleNamespace(stdout=" "),
)
assert client.request(["repos/x/y"]) is None
monkeypatch.setattr(
module.subprocess,
"run",
lambda *args, **kwargs: SimpleNamespace(
returncode=1,
stderr="authentication failed for secret-token",
stdout="",
),
)
with pytest.raises(RuntimeError) as exc_info:
client.request(["repos/x/y"])
assert "secret-token" not in str(exc_info.value)
assert "[REDACTED]" in str(exc_info.value)


def test_load_event_and_main_paths(tmp_path: Path, monkeypatch, capsys) -> None:
Expand Down
Loading