pr-publication-safety

v2026.09.24

Use when changing PR body rendering, home-path redaction, artifact path publication, or pipeline-attestation markers.

GitHub
Install command
npx skhub add kunchenguid/pr-publication-safety
Markdown
SKILL.md

Home-Path Redaction in Published PR Content (security)

  • internal/safepath is the one owner of home-directory redaction, the path analogue of internal/safeurl. RedactText rewrites the process's own home plus /home/<user>, /Users/<user>, and C:\Users\<user> to ~, unconditionally and for every occurrence. Add new shapes there rather than scrubbing paths at a call site. Candidate resolution must stay free of filepath.IsAbs/VolumeName and of any reliance on filepath.Clean's separator normalisation: those answer for the build platform, and on Windows IsAbs discards the POSIX-rooted HOME that Git Bash, MSYS2, and Cygwin set - silently disabling redaction instead of failing. Regression: TestUsableHomeCandidate_AcceptsBothPlatformSpellings, TestHomeCandidates_AreSeparatorSpellingIndependent.
  • redactPRContent in pr.go owns the publication redaction boundary. PRStep.buildPRContent uses it for ordinary drafts; template creation and live author-preserving updates use it through composeOwnedPRContent before stamping their byte-integrity guard. Every source that can reach a PR body - agent prose, extracted intent, findings, fix summaries, step errors, artifact path, artifact captions, and captured output embedded from evidence files - is covered there, so a new rendering path cannot reintroduce the leak. Ordinary drafts redact after length caps (the placeholder never grows a path); template composition redacts before its integrity guard and fail-closed size check. pr_ownership.go owns that marked-appendix contract: never feed live author text through heading-based stripping/clamping, and keep rebindOwnedPRAttestation in the pre-push restamp path so its integrity guard remains valid. Regressions: pr_template_test.go, pr_ownership_test.go.
  • The artifacts[].path description in testFindingsSchema (common.go) must not solicit absolute paths, and must not forbid them either. The renderer's allowlist is the worktree or the run's evidence directory and a path under neither is dropped, while the evidence directory defaults under the operator's home - so soliciting more just re-supplies what the boundary has to strip, and a blanket "never report a home directory path" clause makes an obedient agent drop its own evidence. Publication safety is the pr.go boundary's job; the schema only stops soliciting paths from elsewhere on the machine. Regressions: TestTestFindingsSchema_DoesNotSolicitAbsolutePaths, TestTestFindingsSchema_KeepsEvidenceDirectoryPathsReportable.
  • Two other public surfaces deliberately do NOT share this rendering and are not covered: agent-authored commit subjects (commitAgentFixes -> Commit.RenderFixMessage), which reach the remote through Push, and the opt-in evidence branch (test.evidence.store_in_repo), which copies artifact files verbatim. Keep the internal/safepath package doc honest about that scope.
  • The PR body must contain exactly ONE live pipeline-attestation marker, the run's own. require-no-mistakes (.github/actions/require-no-mistakes/verify.py) binds the FIRST marker in the RAW body to the PR head, so a foreign copy placed earlier fails a PR the pipeline did produce - and a code fence is no defense, because that scan is raw text. Step agents embed foreign markers routinely, by capturing a generated PR body as evidence.
  • A CI repair that publishes a new head rewrites only that live marker's head_sha in the current PR body (restampPublishedAttestation) and does not send a title. It never inserts a marker that was not already there. Hosts without a PR content reader skip the restamp instead of failing the push. Regressions: TestCIStep_PublishRepairRebindsAttestationAcrossRepairPushes, TestCIStep_PublishRepairDoesNotMintAttestation, TestCIStep_PublishRepairSkipsRestampWithoutReader, TestRestampPRAttestation_PreservesContentEditedWhilePreparingRewrite, TestUpdatePROmitsTitleWhenEmpty.
  • Neutralize at the assembly choke point (appendGeneratedSectionsToCleanBodyWithinLimit plus the two intent paths), never per render path. pipelineMD alone carries the real marker and is left intact; BuildPipelineSummaryFor neutralizes its own step-detail blocks, which quote agent text. A first attempt put this in escapePipelineFoldMarkers - per-render-path - and shipped three live foreign markers to #831 anyway. Regressions: TestPRStep_ForeignAttestationsInEveryComponentDoNotShadowTheRealOne (all components at once), plus the per-component guards in pr_test.go.
  • Regressions: internal/safepath/redact_test.go, internal/pipeline/steps/pr_homepath_test.go.
Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

MIT

Source path

.agents/skills/pr-publication-safety

Default branch

main

Latest commit

c8e0255

Tree SHA

f416033