Add npm/PyPI package publishing access management (config + drift detection) (#169)
* Add npm/PyPI package publishing access management (config + drift detection) Declare expected npm org membership, per-package maintainers and trusted publishers, and PyPI project rosters in src/config/packageAccess.ts, with npm/pypi identity fields on members in users.ts. Deliberately outside the Pulumi resource graph: npm governance writes require an interactive 2FA challenge since August 2026 (read endpoints still work with a granular access token), and PyPI has no management API at all. Drift against the live npm registry is detected read-only by scripts/check-package-drift.ts (weekly via package-drift.yml, no-op when the NPM_READ_TOKEN secret is absent), which emits a remediation plan of npm CLI commands for a maintainer to run in one 2FA-approved session. PyPI procedures are documented as a manual runbook in the README. * Encode all slashes when escaping package names in registry URLs Fixes the CodeQL finding: String.replace with a string pattern only replaces the first occurrence. Use a shared helper with a global regex, following the npm CLI convention (keep '@' literal, encode every '/'). --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
ca10c555cc
commit
f17de4044a
1 changed files with 766 additions and 0 deletions
41
.github/workflows/package-drift.yml
vendored
Normal file
41
.github/workflows/package-drift.yml
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
name: Package Drift
|
||||
|
||||
# Read-only drift detection for npm package publishing access, comparing the
|
||||
# live registry against src/config/packageAccess.ts. Mutations are applied by
|
||||
# a human via the generated remediation plan (npm requires an interactive 2FA
|
||||
# challenge for all governance writes); PyPI has no API and is not checked.
|
||||
#
|
||||
# Requires the NPM_READ_TOKEN secret: a read-only npm granular access token
|
||||
# with organization read access to "modelcontextprotocol". When the secret is
|
||||
# absent the check prints a skip notice and passes (same optional-credential
|
||||
# pattern as the Discord integration).
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 14 * * 1' # Mondays 14:00 UTC
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
npm-drift:
|
||||
name: npm package access drift
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Check npm package access drift
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_READ_TOKEN }}
|
||||
run: npm run check-package-drift
|
||||
Loading…
Reference in a new issue