Skip to content

[Enhancement]: Release - The winget publish step holds a long-lived cross-repo PAT that the documented supply-chain and threat... #1676

Description

@laurentiu021

Problem

The release job's winget step authenticates with a maintainer-supplied personal access token: .github/workflows/release.yml:180-186 runs the winget-releaser action with token: ${{ secrets.WINGET_TOKEN }}. This is the only secret in the release pipeline that is not the ephemeral, repo-scoped GITHUB_TOKEN (the other secret usages are secrets.GITHUB_TOKEN in auto-release.yml:37 and :122, and secrets.CODECOV_TOKEN in ci.yml:83 and :91) - it must carry write access to a fork of microsoft/winget-pkgs in order to open the upstream manifest PR. It is therefore the highest-value credential in the distribution chain, and the winget manifest is the one channel where the binary URL and hash users receive are asserted outside this repo. Yet SECURITY.md's supply-chain section (lines 143-150) does not mention it, and the threat model scopes "Update channel attacks (spoofed releases, signature bypass)" as in-scope (SECURITY.md:161) while describing only two distribution paths - the GitHub Releases API (SECURITY.md:109-116) and the portable/winget package (SECURITY.md:117-124) - without acknowledging the credential that publishes the latter.

Proposed solution

Add a bullet to SECURITY.md:143-150 recording the winget publishing posture: that release automation uses a separate token whose only purpose is opening a manifest PR against the maintainer's winget-pkgs fork, that it is scoped as narrowly as GitHub permits (a fine-grained PAT limited to that single fork with contents write, rather than a classic full-repo token), and that it carries an expiry and is rotated. Verify the live token actually matches that description before documenting it, and set a calendar expiry so rotation is forced rather than remembered. Nothing about the workflow needs to change if the token is already fine-grained - the gap being closed is that the posture is undocumented and therefore unverifiable by a reader.

Rationale

SECURITY.md invites vulnerability reports and explicitly puts update-channel attacks in scope, so it should account for every credential that can influence what a user installs. A compromised or over-scoped WINGET_TOKEN is the one path by which a third party could get a manifest pointing at a binary of their choosing in front of users who install via the headline winget install laurentiu021.SysManager command (README.md:29) - a path that the app's own SHA256 self-update check cannot see, because it only ever validates the GitHub Releases asset. Documenting the scoping and rotation makes the strongest link in the chain auditable; if the audit shows the token is a classic full-scope PAT, narrowing it is a five-minute fix with real risk reduction.

Evidence

Read .github/workflows/release.yml:180-186 in full - - name: Update winget package / uses: vedantmgoyal9/winget-releaser@4ffc... / identifier: laurentiu021.SysManager / token: ${{ secrets.WINGET_TOKEN }}. Enumerated every secret in the pipeline: grep -rn "secrets\." .github/workflows/ -> auto-release.yml:37 and :122 (GITHUB_TOKEN), ci.yml:83 and :91 (CODECOV_TOKEN), release.yml:186 (WINGET_TOKEN) - WINGET_TOKEN is the only non-ephemeral, cross-repo one. Read SECURITY.md:143-150 (supply chain: three bullets, no token mention) and SECURITY.md:152-161 (Scope: "Update channel attacks (spoofed releases, signature bypass)" in scope). Grepped SECURITY.md for token/secret/PAT/fork -> only line 159 "Credential or token exposure (shouldn't apply - the app stores neither)", which is about the app's runtime, not the release pipeline. Confirmed README.md:29 makes winget the headline install path.

Risk / trade-off

Documenting the existence of a high-value token slightly advertises a target, though the workflow file is already public and the token reference is visible in it - so the disclosure is nil and the transparency is a net gain. The real work is the audit of the live token's actual scope, which cannot be verified from the snapshot; if it turns out to be a classic full-repo PAT, narrowing it risks breaking the winget step on the next release and should be validated with a manual workflow_dispatch rather than discovered during an automated release.

Affected area

Release


Effort: S | priority: value 3/5, fit 4/5
Identified during the trust, distribution and reach audit audit.

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentationenhancementNew feature or requestsecuritySecurity issue

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions