fix(safe-outputs): support GitHub App comment replacement - #2082
Conversation
Capture the App bot identity during token minting so hide-older-comments does not call endpoints forbidden to installation tokens. Add compiler, executor, and E2E coverage for the actor handoff. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aa416c50-4e54-4a23-b35a-a6f5be7795a6
|
Azure Pipelines: Successfully started running 2 pipeline(s). There may be pipelines that require an authorized user to comment /azp run to run. |
|
/review |
|
✅ Compiler Contract Reviewer completed the compiler contract review.
|
There was a problem hiding this comment.
No merge-blocking Rust issues found in this diff.
Scope and method
Reviewed src/compile/{agentic_pipeline,common,types}.rs, src/compile/extensions/ado_script.rs, src/safe_outputs/{comment_on_github_issue,create_pull_request,github_api,result,upload_build_attachment}.rs, and tests/compiler_tests.rs.
- Error handling: the
?propagation throughcomment_on_github_issue_config()?and the new fail-closed empty-actor-login check (returnsExecutionResult::failurebefore any comment mutation) are both sound. - Correctness:
github_actor_requiredgating correctly combines thecomment-on-github-issuetool check withhide_older_comments, and is threaded consistently intogithub_app_token_step_typed_for,generate_executor_ado_env, and the executor context (ExecutionContext::github_actor_login). - The removed
authenticated_comment_actor/GithubInstallationmachinery is fully superseded by the compile-time actor handoff, and tests were updated in lockstep (mock servers now assert/userand/installationare never hit). - No unsafe casts, no blocking-in-async, no HashMap-ordering nondeterminism introduced by these changes.
- I also ran a
rust-criticsub-agent pass over the same diff in parallel; it returned no findings.
🦀 Rust code quality review by Rust Code Quality Reviewer · auto · 48.5 AIC · ⌖ 2.55 AIC · ⊞ 11.5K
Comment /review to run again
There was a problem hiding this comment.
Security review — no regressions found
This diff improves the security posture rather than weakening it: Stage 3's comment-on-github-issue executor no longer sends the write-capable GitHub App installation token to /user or /installation for actor discovery. Instead, the App bot login (<slug>[bot]) is derived once during JWT-authenticated token minting (which already calls /orgs/{owner}/installation and receives app_slug in the response) and passed to the executor as a non-secret ADO job variable (ADO_AW_GITHUB_ACTOR_LOGIN, set via setVar, not setSecretVar).
Checked and confirmed sound:
executor_actor_var()only returnsSomefor theAppauth variant — PAT-backed configs never get this env var and continue to resolve viaGET /user, matching the docs.- The new actor-login path fails closed: an empty/whitespace
ADO_AW_GITHUB_ACTOR_LOGINreturnsExecutionResult::failurebefore any comment listing/write calls (verified byhide_older_rejects_empty_app_actor_before_comment_writes, which asserts only the initial issue-fetch request was made). --actor-output-var's value is a compiler-owned constant (ADO_AW_SAFE_OUTPUTS_GITHUB_APP_ACTOR_LOGIN), not user-controlled input, and is single-quoted via the existingsh_single_quotehelper consistent with other argv flags — no injection surface introduced.- No secret (JWT, installation token) is logged or exposed through the new variable; the login string itself is non-sensitive.
- Stage separation is preserved: the mint step (JWT auth) and comment-hiding write (installation token) both still run in Stage 3 (SafeOutputs), so no read-only Stage 1 boundary is touched.
No exploitable weaknesses identified. Approving is not permitted for this workflow, so filing as COMMENT.
🔒 Security review by PR Security Reviewer · auto · 46.9 AIC · ⌖ 2.82 AIC · ⊞ 11.7K
Comment /review to run again
There was a problem hiding this comment.
TypeScript review — scripts/ado-script/
Reviewed github-app-token/index.ts (install-metadata capture, --actor-output-var, [bot] login derivation) and the new comment-on-github-issue-hide-older executor-e2e scenario.
No high-confidence defects found:
resolveInstallationvalidatesdata.idis numeric before use and safely narrowsapp_slug(string-checked + trimmed) before exposing it; both success and app_slug-missing failure paths are covered by new tests.- The actor login is derived and validated before
mintInstallationTokenruns, so a missingapp_slugfails closed without ever minting/exposing a token — good ordering. setSecretVaris still called beforesetVar/logInfo, preserving the existing secret-before-log-line invariant.setVar/setSecretVaralready escape name and value (escapeProperty/escapeMessage) so the new non-secretactorOutputVarvariable emission is not an injection vector.- New executor-e2e scenario follows the existing
MutationIssueState/requireGraphqlFeaturepattern used by sibling scenarios (e.g.hideGithubIssueComment) with proper cleanup/teardown.
Note: the ts-critic sub-agent produced no parseable JSONL output after being prompted twice, so its findings could not be incorporated — this review reflects only my own pass over the diff.
No merge-blocking issues.
🟦 TypeScript code quality review by TypeScript Code Quality Reviewer · auto · 71.1 AIC · ⌖ 2.63 AIC · ⊞ 11.4K
Comment /review to run again
There was a problem hiding this comment.
Solid, well-targeted test rewrite: the removed authenticated_comment_actor//installation discovery path is cleanly replaced by explicit ctx.github_actor_login handling, and both the mint-derived-actor success path and the empty-actor-login rejection are directly tested (hide_older_uses_mint_derived_app_actor_without_discovery, hide_older_rejects_empty_app_actor_before_comment_writes), including an assertion that no /user or /installation calls are made. The TS resolveInstallation rename and actorOutputVar/app_slug-missing fail-closed behavior are also directly tested.
Two untested edges left inline: the PAT fallback's error arm (client.authenticated_user() failing, e.g. 403) isn't exercised for the hide-older branch, and an empty-string app_slug (vs. absent) isn't covered in resolveInstallation/main. Neither is merge-blocking, but both are cheap to add and close real gaps around this fail-closed identity logic.
🧪 Test quality analysis by Test Quality Sentinel · auto · 106.6 AIC · ⌖ 1.95 AIC · ⊞ 11.3K
Comment /review to run again
There was a problem hiding this comment.
Compiler contract review — no blocking issues
Reviewed the actor-identity handoff for hide-older-comments under safe-outputs.github-app:
- File-list drift check:
pr-meta.json.filesincludes no gate/fact IR files (filter_ir.rs,Factenum untouched) and no.gen.ts/.gen.jsonfiles — no codegen drift expected. No.github/workflows/*.mdchanged without its.lock.yml. No committed.lock.ymladded undertests/safe-outputs/.scripts/ado-script/*.jsbundles correctly absent (gitignored build artifacts). - Contract wiring verified end-to-end:
SAFE_OUTPUTS_GITHUB_APP_ACTOR_LOGIN_VAR/SAFE_OUTPUTS_GITHUB_ACTOR_LOGIN_ENVconstants insrc/compile/types.rsare threaded throughgithub_app_token_step_typed_for→generate_executor_ado_env→ExecutionContext.github_actor_login→comment_on_github_issue.rs, with thegithub_actor_requiredflag correctly scoped perSafeOutputsVariant(confirmedtest_compile_github_app_auth_is_scoped_to_reviewed_variantandtest_compile_github_app_hide_older_wires_actor_identitypass, and the non-hide-older App fixture asserts absence of--actor-output-var/ADO_AW_GITHUB_ACTOR_LOGIN). - Fail-closed behavior: empty/whitespace
ADO_AW_GITHUB_ACTOR_LOGINis rejected before any comment write (hide_older_rejects_empty_app_actor_before_comment_writes), and the removedauthenticated_comment_actor//installationfallback is fully replaced rather than left as dead code. - Docs: both
docs/ado-script.mdanddocs/safe-outputs.mdupdated to describe the new actor-derivation path. - Ran
cargo build --bin ado-awand the new/adjacentcompiler_tests(test_compile_github_app_hide_older_wires_actor_identity,test_compile_github_issue_app_fixture_scopes_tokens_by_stage,test_compile_github_app_auth_is_scoped_to_reviewed_variant) locally — all pass.
No raw-String identifier fields introduced (the new actor_output_var/github_actor_login are plain bot-login strings, not paths/refs/SHAs, so src/secure.rs newtyping doesn't apply here). No missing extension registration, no IR/summary contract changes. Nothing merge-blocking found from a compiler-contract perspective.
🏗️ Compiler contract review by Compiler Contract Reviewer · auto · 82.3 AIC · ⌖ 2.09 AIC · ⊞ 12.8K
Comment /review to run again
Summary
hide-older-comments/userand/installationactor discovery for installation tokens while preserving PAT/userbehavior and fail-closed hide-before-post orderingFixes #2079
Test plan
cargo testcargo clippy --all-targets --all-features -- -D warningsnpm testinscripts/ado-scriptnpm run typecheckinscripts/ado-scriptnpm run build:github-app-tokeninscripts/ado-script