- Remove top banner and add a tagline under the H1 so the value
proposition sits above the fold; move the demo GIF above the badges.
- Reframe "How PMG Works" as defense in depth with three explicit
layers (Threat Intelligence, Policy/Cooldown, Sandbox) and promote
Dependency Cooldown out of the Features table.
- De-duplicate install instructions between Quick Start and Installation,
and drop the all-"Yes" Status column from the supported package
managers table.
- Update GitHub Actions snippet to actions/setup-node@v6 and add a
comment recommending SHA pinning for third-party Actions.
- Add Mini Shai-Hulud (300+ npm packages compromised) to the recent
malicious-package examples.
- Tighten prose: remove filler adverbs, passive voice, em-dashes, and
marketing fluff; split semicolon-joined clauses into separate
sentences.
* feat: add GitHub Action for one-step PMG setup in CI
Composite action at repo root that downloads PMG (with SHA-256 verification
against the upstream checksums.txt), runs `pmg setup install`, and wires
shims onto $GITHUB_PATH so subsequent `npm install` / `pip install` calls
are transparently analyzed.
Defaults are conservative: malware blocking + dependency cooldown + proxy
mode (matching PMG's own defaults). Sandbox is opt-in because enabling
Landlock/Bubblewrap on ubuntu-latest requires relaxing AppArmor
user-namespace restrictions.
Cloud sync uses the documented SAFEDEP_API_KEY / SAFEDEP_TENANT_ID env-var
fallback so we skip the keychain codepath that has no usable backend in
headless CI. When cloud is enabled and no endpoint-id is supplied, the
action sets PMG_CLOUD_ENDPOINT_ID=github-actions/${GITHUB_REPOSITORY} so
events aggregate per repository instead of per ephemeral runner hostname.
Closes#248.
https://claude.ai/code/session_01ARb8ZiBiJjvhWjchBXraAh
* fix(action): drop github.repository template from input description
Action manifest validation rejected the action.yml because the endpoint-id
input description contained ${{ github.repository }} — template expressions
aren't evaluated in input description text and trip the validator with
"Unrecognized named-value: 'github'". This caused every job using uses: ./
to fail before any step ran.
Also switch the config-file e2e job to verify the staged file directly
instead of calling `pmg config get`, which is not in the v0.13.0 release
that "latest" resolves to today.
https://claude.ai/code/session_01ARb8ZiBiJjvhWjchBXraAh
* fix(action): address review comments on PR #263
- Drop opinionated defaults on PMG_* toggle inputs. All defaults are now
empty strings, and the action only exports PMG_* env vars when the
caller explicitly sets the input. Without this, defaults like
PMG_PARANOID=false silently shadowed config-file overrides because env
vars beat config.yml in Viper precedence.
- Verify cached PMG against upstream checksums.txt on every cache hit.
The cached tarball is stored alongside the binary and re-hashed against
the freshly-fetched checksums.txt; on drift, the cache entry is evicted
and re-downloaded.
- Export PMG_* env vars BEFORE running `pmg setup install` so settings
like disable-telemetry actually apply during setup, not just to
subsequent package-manager calls.
- Add `|| true` to the grep that extracts the expected checksum so
set -e doesn't kill the script before the friendly error message fires
when no checksum entry is found.
- Pin third-party actions (actions/checkout, actions/setup-node) to
commit SHAs to match the repo's supply-chain hardening convention.
- Fix the malicious-package E2E test capturing tee's exit code instead
of npm's; redirect to a file and check the actual command exit code.
- Add an E2E job that asserts PMG_PARANOID is unset when only
config-file is provided — regression guard for the precedence fix.
https://claude.ai/code/session_01ARb8ZiBiJjvhWjchBXraAh
* ci(action-e2e): scope sandbox tests to action setup, not PMG runtime
The landlock job was running `npm install express` with no explicit
sandbox profile and the default profile blocks something npm needs
(PMG's own e2e uses `--sandbox-profile npm-restrictive` to make this
viable). Bubblewrap happened to pass, but verifying the default sandbox
profile is permissive enough for arbitrary package installs is PMG's
e2e responsibility — this workflow's job is to assert the action wires
sandbox config correctly.
Switch both drivers to a matrix and verify only what the action owns:
PMG_SANDBOX_* env vars propagated, pmg binary runs, bwrap is installed
when requested, AppArmor user-ns restriction relaxed.
https://claude.ai/code/session_01ARb8ZiBiJjvhWjchBXraAh
* ci(action-e2e): bump setup-node to 24
Node 20 reached end-of-life and setup-node now warns on it. Match the
version pinned by publish-npm.yml (the repo's newest workflow). Updated
the README and docs/github-action.md quick-start examples to match.
https://claude.ai/code/session_01ARb8ZiBiJjvhWjchBXraAh
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat: Add proxy_install_only config to restrict proxy to download commands
Introduces proxy_install_only (default: false) which, when enabled,
skips the proxy for package manager commands that do not download
packages (e.g. npm ls, pip list), avoiding unnecessary MITM overhead.
- Add ProxyInstallOnly to Config and config template
- Add IsKnownDownloadCommand / MayDownloadPackages to ParsedCommand
- Add DownloadCommands to npm and pypi PM configs covering update,
ci, audit, dlx, exec, x, download, run and equivalents per PM
- Extract shared runner.Execute used by both proxy flow and guard
- Proxy flow short-circuits to runner.Execute for non-download commands
when proxy_install_only=true
* refactor: Inject CommandExecutor into guard to fix dependency direction
guard depended on internal/runner, which inverted the intended layer
hierarchy. Now guard defines a CommandExecutor function type and accepts
it as a constructor argument. internal/flows (the composition root)
creates the executor closure wrapping runner.Execute and injects it,
keeping guard free of internal/ dependencies.
* refactor: Invert proxy_install_only logic to use known non-download commands
Replace the DownloadCommands allowlist (opt-in, fail-open) with a
NonDownloadCommands denylist (opt-out, fail-safe). The proxy now runs
for all commands except those explicitly known to not download packages.
Unknown or future package manager subcommands default to running with
the proxy.
Includes script runners (run, start, test, stop, restart) that can spin
up local servers — setting proxy env vars on these breaks them without
providing any security benefit. Also covers removal commands and local
operations that never contact the registry.
* fix: Support PMG_* env vars regardless of config file state
AutomaticEnv only resolves env vars for keys Viper already knows about
via AllKeys(). When a key is absent from the config file (commented out,
new key added after last setup, or no config file at all), Viper had no
knowledge of it and silently skipped the env var.
Fix by registering all Config struct fields as Viper defaults via
reflection (using mapstructure tags) before reading the config file.
This ensures PMG_* env vars work in all cases.
Precedence: cobra flags > env vars > config file > defaults.
SetDefault is used (not Set) so env vars and config file can still
override the Go defaults freely.
Tests added covering all precedence levels including the key-absent-
from-config-file case that was the original bug report.
* fix: Only check first non-flag arg against NonDownloadCommands
Scanning all args caused false proxy bypasses when package names or
script arguments matched a NonDownloadCommands entry. For example:
- npm exec test → "test" matched, proxy incorrectly skipped
- npm update config → "config" matched, proxy skipped
- npm publish --tag version → "version" matched, proxy skipped
Fix by checking only the first non-flag argument (the subcommand).
If it is not in NonDownloadCommands we break immediately, so trailing
args never influence the classification. Applied to all four parsers:
npm, pip/pip3, uv, and poetry.
Regression tests added for the false positive cases.
* refactor: Replace reflection-based Viper defaults with embedded template
Load the embedded config template as the Viper base so all keys are
registered upfront, enabling PMG_* env vars to work regardless of
whether a key exists in the user's config file.
* fix: Restore trusted_packages template entry and revert DefaultConfig change
* docs: Document environment variable overrides for config keys
* update npm test cmd
* refactor: extract shared non-download command detection helper
Replaces duplicated first-non-flag-arg detection loops in npm.go and
pypi.go (pip + poetry parsers) with a shared isFirstNonFlagArgInList
helper in packagemanager.go.
https://claude.ai/code/session_01AHaKF3vc2Haj9tK3jgUBAs
---------
Co-authored-by: Claude <noreply@anthropic.com>
* update README.md
* ci: Remove paths-ignore filters from all workflows
Removes docs/ and *.md path exclusions so required checks always run,
preventing PRs from being blocked when only documentation files change.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* docs: Add config merging design spec for #114
Defines the merge-during-setup-install approach for keeping user
configs up to date with new template keys while preserving all
existing values, comments, and formatting.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: Add implementation plan for config merging
TDD-based plan with 6 tasks: dependency setup, failing tests,
core merge implementation, integration test, WriteTemplateConfig
integration, and full verification.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: Merge template config into existing user config during setup install
Instead of skipping when a config file exists, WriteTemplateConfig() now
merges missing keys from the embedded template into the user's config
using YAML AST manipulation. Preserves all user values, comments, and
formatting. Only adds keys present in the template but absent in the
user's config.
Closes#114
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: Graceful error handling for config loading and setup commands
Replace panics in loadViperConfig with error returns so the app falls
back to defaults instead of crashing on malformed config files. Add
SilenceUsage to setup install/remove commands so runtime errors don't
dump the full usage text.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* add test cmd in readme
* update copy text
* refactor: Address review feedback on config merging
- Rename existing/template to dest/source for generic util naming
- Remove unnecessary code comments (Rule N references, obvious comments)
- Add AGENTS.md with dev guide and code style rules, symlink CLAUDE.md
- Add BenchmarkMergeYAML (~46μs/op on M4 Pro)
- Remove stale design spec
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* update `MergeYAML` to use from dry/utils
* update AGENTS.md
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: Add support for bubblewrap sandbox
* fix: Glob pattern expansion limit for linux
* fix: Bug in glob pattern expansion for bwrap
* fix: README on trust
* fix: Multiple bubblewrap translator fix
* test: Add E2E for linux sandbox
* fix: Refactor bwrap sandbox to use common dangerous files
* fix: Path test case
* fix: Non-existent path handling bug
* refactor: Misc cleanup
* fix: Avoid bind mount for non-existentent deny protection
* fix: Off by one bug in path depth handling
* ci: Disable AppArmor on GHA runner
* fix: Disable apparmor userns restrictions
* define contract for package executors
* introduce npx executor
* add npx and pnpx cmd support
* fix typo
* rm PackageExecutor and depend on PackageManager interface
* add support for PTY to handle parent-child process interaction
* refactor PTY handling in proxy flow
* enforce interactiveSession interface check
* close reader explicitly and clean npm version for pkg executors
* rm interaction from interceptors
* add docs and wait for outputRouter before exit
* add support for non interactive TTY for proxy mode
* add support for CI env var check for non interactive tty proxy mode
* update readme to include npx, pnpx support
* Update internal/flows/proxy_flow.go
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Sahil Bansal <bansalsahil315@gmail.com>
* update ptyx lib
* fix docs typo
---------
Signed-off-by: Sahil Bansal <bansalsahil315@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* feat: Add experimental proxy based npm interceptor
* refactor: Analysis cache
* ci: Add E2E for npm proxy
* fix: Handle dry-run in proxy flow
* fix: Handle special case for scope package name
* fix: Misc fixes
* fix: Code review fixes
* fix: Code review fixes
* refactor: Reusable code into base registry interceptor
* Pause npm process during user confirmation (#90)
* pause npm process when prompting user for confirmation
* disable progress bar
* fix logging and close chan on return
* update use of deprecated field
* refactor: Separation of concerns for handling process state
* fix: Safe permission for cert file
* fix: Handle nil check for interaction hook
* fix: Add test for base registry
* Fix goreleaser for windows build (#93)
* introduce platform specific process control
* rename common.go to common_flow.go
* feat: Add support for pause resume on windows
* fix: Code review fixes
* test: Add confirmation handler tests
---------
Co-authored-by: Sahil Bansal <bansalsahil315@gmail.com>
* Add support for yarn package manager
- Added yarn command handler in cmd/npm/yarn.go
- Added DefaultYarnPackageManagerConfig() in packagemanager/npm.go
- Added yarn dev flag support (--dev/-D) in ParseCommand()
- Added yarn analytics tracking in internal/analytics/event.go
- Registered yarn command in main.go
- Added yarn to alias setup in internal/alias/alias.go
- Added comprehensive test suite in packagemanager/yarn_test.go
- All tests passing (12 yarn tests + existing tests)
Resolves#30
Signed-off-by: shrvansudhakara <shrvansudhakara@gmail.com>
* docs: update README with yarn package manager support
Updated documentation to reflect yarn support addition:
- Changed yarn status from '🚧 Planned' to '✅ Active' in package managers table
- Added 'pmg yarn add <package>' command example
- Added 'yarn add <package-name>' to command list
- Added 'yarn install # Uses yarn.lock' to lockfile examples
This documentation update corresponds to the yarn implementation in the previous commit.
Related to #30
Signed-off-by: shrvansudhakara <shrvansudhakara@gmail.com>
* fix: address review feedback for yarn support
- Add support for bare 'yarn' command as manifest install
- Validate yarn install does not accept package names
- Add YarnExtractor for yarn.lock file support
Changes:
- packagemanager/npm.go: Added bare yarn command handling and validation
- packagemanager/yarn_test.go: Added test for bare yarn command
- extractor/ecosystems.go: Added Yarn PackageManagerName and extractor
- extractor/npm.go: Added YarnExtractor implementation
- go.mod, go.sum: Updated dependencies
Addresses review feedback from @Sahilb315
- Bare yarn command now triggers manifest install per docs
- yarn install <package> is now properly rejected
- yarn.lock extraction support added
Related to #30
Signed-off-by: shrvansudhakara <shrvansudhakara@gmail.com>
* chore: tidy go.mod and go.sum to remove unused dependencies
Signed-off-by: shrvansudhakara <shrvansudhakara@gmail.com>
* fix: add yarn.lock extractor mapping
- Add yarn.lock case in getExtractorForFile switch
- Import yarnlock extractor from osv-scalibr
- Enables lockfile parsing for manifest-based installations
Tested both package-based and manifest-based installations successfully.
Addresses review feedback from @Sahilb315
Signed-off-by: shrvansudhakara <shrvansudhakara@gmail.com>
* test: add validation test for yarn install with package name
- Add test case to verify 'yarn install <package>' is rejected
- Ensures invalid syntax returns no install targets
- Confirms IsManifestInstall is false for invalid commands
Addresses review feedback from @Sahilb315
Signed-off-by: shrvansudhakara <shrvansudhakara@gmail.com>
---------
Signed-off-by: shrvansudhakara <shrvansudhakara@gmail.com>
* follow proper consistent naming in pypi packagemanager
* follow proper consistent naming in pypi packagemanager - 2
* feat: add specialized command parsers to handle pip and uv command formats
* add uv support & modify extractor to be more robust
* add uv alias
* refactor var name & add error handling
* update readme & add support for `uv pip sync` cmd