fix(safe-outputs): upload-pipeline-artifact uses $(System.AccessToken) (#471)#835
fix(safe-outputs): upload-pipeline-artifact uses $(System.AccessToken) (#471)#835jamesadevine wants to merge 1 commit into
Conversation
#471) Closes #471. Root cause ---------- Build 610504 failed at PUT /_apis/resources/Containers/{id} with HTTP 404 ContainerWriteAccessDeniedException. The ADO File Container ACL is keyed on the build's job-plan identity (Project Build Service account). When permissions.write: is set, Stage 3 overrides SYSTEM_ACCESSTOKEN with $(SC_WRITE_TOKEN), an ARM SPN bearer minted via AzureCLI@2. ARM SPNs are never in the File Container ACL regardless of project-level RBAC, so the PUT is rejected with 404. Phase 1 - surgical fix ---------------------- * New SYSTEM_TOKEN_SAFE_OUTPUTS category containing UploadPipelineArtifactResult. Handlers in this category are excluded from WRITE_REQUIRING_SAFE_OUTPUTS and from the validate_write_permissions gating. * generate_executor_ado_env now emits ADO_SYSTEM_ACCESS_TOKEN: $(System.AccessToken) alongside the existing SYSTEM_ACCESSTOKEN: $(SC_WRITE_TOKEN) whenever any SYSTEM_TOKEN_SAFE_OUTPUTS handler is configured. * ExecutionContext gains a system_access_token field, populated from ADO_SYSTEM_ACCESS_TOKEN. * upload_pipeline_artifact reads system_access_token first and falls back to access_token. Every other handler is bit-identical. Orthogonal cleanup ------------------ * Removed build_id and allowed-build-ids from upload-pipeline-artifact. The File Container ACL constraint makes cross-build publishing impossible; the fields never worked and silently rejected requests at runtime. Forward look - Phase 2 ---------------------- This PR is the first half of a larger gh-aw-aligned redesign. Phase 2 will introduce per-handler service-connection: declarations, a block-level safe-outputs.service-connection: default, and a codemod that auto-migrates the top-level permissions: schema. The SYSTEM_TOKEN_SAFE_OUTPUTS category introduced here becomes the mechanism for "use the Build Service identity" handlers in that redesign. Validation ---------- * cargo build * cargo test (1758 unit + integration, zero failures) * cargo clippy --all-targets --all-features (zero warnings) * tests/safe-outputs/upload-pipeline-artifact.lock.yml regenerated; the env block contains both SYSTEM_ACCESSTOKEN: $(SC_WRITE_TOKEN) and ADO_SYSTEM_ACCESS_TOKEN: $(System.AccessToken). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🔍 Rust PR ReviewSummary: Looks good — the fix is correct, well-scoped, and the test coverage is thorough. Findings
|
Closes #471.
Root cause
Build 610504 failed at
PUT /_apis/resources/Containers/{id}withHTTP 404 ContainerWriteAccessDeniedException.The ADO File Container ACL is keyed on the build''s job-plan identity (the Project Build Service account). When
permissions.write:is set, Stage 3 currently overridesSYSTEM_ACCESSTOKENwith$(SC_WRITE_TOKEN)— an ARM SPN bearer minted viaAzureCLI@2. ARM SPNs are never in the File Container ACL regardless of project-level RBAC, so the PUT is rejected with 404 (ADO returns 404 instead of 403 for ACL denials).Phase 1 — surgical fix
This PR unblocks #471 with a minimal change scoped to
upload-pipeline-artifact:SYSTEM_TOKEN_SAFE_OUTPUTScategory insrc/safeoutputs/mod.rscontainingUploadPipelineArtifactResult. Handlers in this category are excluded fromWRITE_REQUIRING_SAFE_OUTPUTSand fromvalidate_write_permissions''s gating.generate_executor_ado_envnow emitsADO_SYSTEM_ACCESS_TOKEN: $(System.AccessToken)alongside the existingSYSTEM_ACCESSTOKEN: $(SC_WRITE_TOKEN)whenever anySYSTEM_TOKEN_SAFE_OUTPUTShandler is configured.ExecutionContextgains asystem_access_tokenfield, populated fromADO_SYSTEM_ACCESS_TOKEN.upload_pipeline_artifactreadssystem_access_tokenfirst and falls back toaccess_token. Every other handler is bit-identical.The change is deliberately narrow: 13 other write-requiring handlers continue to use the ARM SPN token. No schema change, no codemod, no deprecation window.
Orthogonal cleanup
build_idandallowed-build-idsfromupload-pipeline-artifact. The File Container ACL constraint makes cross-build publishing impossible; the fields never worked and silently rejected requests at runtime. Documented in the rewritten module docstring.Forward look — Phase 2
This PR is the first half of a larger gh-aw-aligned redesign. Phase 2 will introduce per-handler
service-connection:declarations, a block-levelsafe-outputs.service-connection:default, and a codemod that auto-migrates the top-levelpermissions:schema. TheSYSTEM_TOKEN_SAFE_OUTPUTScategory introduced here becomes the mechanism for "use the Build Service identity" handlers in that redesign.Validation
cargo build✅cargo test✅ (1758 unit + integration, zero failures)cargo clippy --all-targets --all-features✅ (zero warnings)tests/safe-outputs/upload-pipeline-artifact.lock.ymlregenerated; the new env block contains bothSYSTEM_ACCESSTOKEN: $(SC_WRITE_TOKEN)andADO_SYSTEM_ACCESS_TOKEN: $(System.AccessToken).upload-pipeline-artifact, so no further lock-file diffs.Docs updated
docs/safe-outputs.md—### upload-pipeline-artifactrewritten (current-build-only; uses$(System.AccessToken); nobuild_id).docs/template-markers.md—{{ executor_ado_env }}updated.docs/network.md— Security Model reframed with theupload-pipeline-artifactexception.