mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
main
29
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
94781d6bda |
Remove guard mode: proxy interception is now the only flow (#386)
* refactor: remove guard mode execution paths and guard-only packages Guard (non-proxy) mode is removed; all package-manager commands now always run the proxy flow. Removes the guard engine, the common flow, the extractor package, the npm/pypi dependency resolvers and the PackageResolver plumbing that only guard mode consumed. The guard package retains only PackageManagerGuardInteraction, which the proxy flow and confirmation interceptors reuse for user prompts. Proxy behavior is unchanged. * refactor: remove proxy opt-out surfaces, guard references in config, action and docs Removes Config.ProxyMode, ProxyConfig.Enabled, IsProxyModeEnabled, the proxy_mode legacy fallback, PMG_PROXY_ENABLED handling and the --proxy-mode / --include-dev-dependencies flags. Proxy interception can no longer be disabled. Also removes the proxy-mode input from the GitHub Action, the proxy-mode doctor check and setup info row, updates the E2E workflow to stop passing --proxy-mode=false, and sweeps guard-mode wording from docs and the config template. The legacy proxy_install_only flat key and PMG_PROXY_INSTALL_ONLY env var remain supported. audit.FlowTypeGuard is kept so previously recorded audit events still translate for cloud sync. * feat: fail loudly when a removed proxy opt-out is still configured A leftover proxy.enabled: false / proxy_mode: false config key or PMG_PROXY_ENABLED=false / PMG_PROXY_MODE=false env var previously meant guard mode; silently ignoring it would switch those users to proxy interception without notice. PMG now exits with an actionable error naming the exact source. Precedence mirrors the old resolution order: env (ignored under lockdown) > proxy.enabled > legacy proxy_mode. The pmg config subtree is exempt so the config file can still be fixed with pmg config edit/set. The GitHub Action's proxy-mode input is kept as a tombstone that fails the action when set to false and warns otherwise. * refactor: extract flows.RunProxy and address review findings Collapses the identical parse-then-run body duplicated across the 12 package manager commands into flows.RunProxy. Documents the cache-hit / offline analysis trade-off versus the removed guard manifest path, fixes a stale non-proxy label in the E2E workflow and a stale guard reference in the uvx parser comment. * fix(config): mirror old proxy opt-out precedence exactly PMG_PROXY_MODE only ever took effect through the legacy fallback, which was gated on the presence of a proxy: key in the config file (even a null one). Promoting it to the top env tier caused two inversions: a stale PMG_PROXY_MODE=false hard-failed configs that resolved to proxy mode, and PMG_PROXY_MODE=true silently overrode an explicit proxy.enabled: false file opt-out. The check now resolves in the old order: PMG_PROXY_ENABLED > proxy: section (presence gates the legacy tier) > PMG_PROXY_MODE > flat proxy_mode. parseOptOutBool also accepts numeric values (0 = false) to match viper's WeaklyTypedInput/cast.ToBool coercion, so proxy.enabled: 0 and proxy_mode: 0 are detected as opt-outs. * refactor: move package manager interaction out of guard * refactor: trim package manager interaction * fix(config): normalize config keys viper-style in proxy opt-out check Viper resolved config file keys case-insensitively and expanded dotted keys, so spellings like Proxy:, Enabled:, a literal proxy.enabled key or Proxy_Mode selected guard mode before the removal. The opt-out check now lowercases keys recursively and nests dotted keys before matching, so those existing opt-outs fail loudly instead of being silently ignored. * refactor: remove inert transitive controls, dead parser state and guard audit variant transitive / transitive_depth lost their only consumers with the dependency resolvers; remove the config fields, flags, template and doc entries, and the report/audit plumbing that misreported transitive analysis as enabled. Remove write-only parser state (PackageInstallTarget.Extras, ParsedCommand.ManifestFiles, ShouldExtractFromManifest); IsManifestInstall stays as it feeds sandbox gating via IsInstallationCommand. Remove audit.FlowTypeGuard and its cloud mapping; guard events recorded by pre-removal versions in an unsynced WAL translate to UNSPECIFIED. * fix: address review findings on the opt-out wiring and cleanups Move the removed-opt-out rejection from the CLI PersistentPreRun into proxyFlow.Run: the check now fires exactly for package-manager runs, so non-install commands (pmg setup remove, doctor, config, version) stay usable to fix or remove an opted-out installation, and future commands inherit or avoid the check by construction instead of by exemption list. Also: make the e2e malicious-package assertion actually fail the job when an install is not blocked, route pmg go through flows.RunProxy, and drop the dead extras return from pypiParsePackageInfo (extras are still stripped from package names). * fix(config): make the removed opt-out check faithful to the old resolution The gate that silenced the legacy proxy_mode surfaces matched the raw proxy key case-sensitively in the old code, while values resolved viper-style (case-insensitive, dotted keys); applying each semantic where the old code did fixes both divergences: a case-variant Proxy: section no longer hides a flat proxy_mode: false opt-out, and a dotted proxy.enabled: false overridden by proxy_mode: true no longer errors. Replace the generic key-tree normalization with two targeted lookups (the check only ever resolves proxy.enabled and proxy_mode), which also makes colliding spellings resolve deterministically. Coerce legacy-tier values cast.ToBool-style so PMG_PROXY_MODE=off style opt-outs are detected, log the config read error instead of swallowing it, and shorten the error to a one-line statement with the specific remedy in the help text. Add lockdown coverage (env inert both directions) and a repeated-run determinism test. * fix(config): fall back to defaults for unrecognized proxy opt-out values The old loader swallowed viper errors and ran on defaults, so values like proxy.enabled: yes or PMG_PROXY_ENABLED=banana silently discarded the whole config and defaulted to proxy. Treat them the same way now: unrecognized values mean the default (proxy on) instead of a hard error, and the doc comment no longer claims the old loader failed loudly. Only values that actually meant guard mode fail. Also check the removed opt-out before the CA trust check in pmg go, restoring the old error precedence: a config problem must not steer the user into an unnecessary OS trust store change. * fix(e2e): PMG_PROXY_MODE assertion must match the legacy gate semantics The runner's setup step writes the template config, which has a proxy: section — and with one present the legacy PMG_PROXY_MODE was always inert, so expecting a loud failure there asserts pre-fidelity-fix behavior. Assert both sides instead: inert (command succeeds) with the standard config, loud failure against an empty config dir where the legacy fallback actually applied. * refactor(config): collapse parseOptOutBool to ParseBool over the string form YAML hands us typed values (bool, int), so route them through fmt.Sprintf %v and strconv.ParseBool instead of a per-type switch. Identical behavior for every recognized value; numbers other than 0/1 now read as no opinion instead of cast.ToBool's nonzero-true, which no real config relies on. |
||
|
|
dc0e3202cc |
feat: Linux system-wide setup (pmg setup install --system) (#377)
* feat: add Linux system-wide setup
Install shared shims, managed configuration, and login-shell PATH integration so golden images and multi-user hosts can protect package installs for every user.
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore: keep local design documents untracked
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: harden and simplify Linux system install
Tighten shim detection, profile repair, and install ordering while
trimming over-specific doctor/info hints from the system-install path.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: clarify system-install doctor alias and shim path checks
Use UserBinDir for PATH checks and pass aliases as not required under
system install without treating that as active interception.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: tighten event-log soft-fail warning prefix
Prefix the warning with [pmg] and drop the redundant continuing clause.
Co-authored-by: Cursor <cursoragent@cursor.com>
* ci: add Linux system-install e2e and pin pnpm for add flake
Cover root system setup, PATH/profile.d, managed config, non-root
interception, and remove. Pin pnpm 11.10.0 on the package-manager e2e
job after an integrity crash on pnpm add.
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore: bump packageManager to pnpm 11.10.0 for e2e
Align package.json with the pnpm version we want in CI so action-setup
stops erroring on a version mismatch after the e2e integrity flake.
Co-authored-by: Cursor <cursoragent@cursor.com>
* ci: use npm init for pnpm e2e to avoid integrity crash
pnpm 11.x `pnpm init` still writes onFail:download; `pnpm add` then
fails after PMG analysis even on 11.10.0. Seed the temp package with
npm init instead.
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore: revert packageManager pin to pnpm 11.1.3
The e2e integrity crash is avoided by npm init; the 11.10.0 bump is
no longer needed.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: harden system-install review findings
Require root-owned, non-group/other-writable pmg for --system install;
allow remove without that validation. Doctor checks npm resolution for
PATH precedence, uses ImpliesInterception instead of message matching,
and documents version-manager shadowing. Pass profile bin dir from the
shim manager and note that system config ignores per-user files.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: harden doctor PATH checks and attribute cloud events by OS user
Doctor now verifies every installed package manager against the shim
directory, and system-install validation only requires a safe parent
directory. Cloud sync records username/uid on invocation context for
multi-user hosts sharing one endpoint.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: address system-install review findings
- shim: make system executable resolution injectable so tests pass under
umask 002; skip the root-owner test when running as root
- doctor: treat resolution into either the system or per-user shim dir as
intercepted, and collapse the shim-in-PATH check to a single call site
- setup: make remove (both --system and per-user) best-effort with
errors.Join so one failed step no longer strands the other artifact
- shim: allow a group-writable install parent dir (Debian/Ubuntu ship
/usr/local/bin as root:staff 2775) while still rejecting world-writable
and non-root-owned parents
- audit: attribute cloud events to SUDO_USER when running under sudo
- docs: drop the soft-fail event-logging claim (hard-fail is retained)
* ci: normalize /usr/local/bin perms before system-install e2e
The GitHub ubuntu-latest runner ships /usr/local/bin world-writable so
tooling can install without sudo. System install correctly refuses a
world-writable dir for the shared binary (any local user could replace
it and hijack every user's npm/pip). No FHS-compliant distro or Docker
image ships it world-writable — it is always root:root 0755 or
root:staff 2775 — so this normalizes only the anomalous CI runner back
to standard perms and still exercises the real /usr/local/bin path.
* fix: actionable remedy for root-created per-user config dir
A pmg run as root with a preserved HOME (GitHub runners, sudo -E, su
without -) creates the invoking user's ~/.config/safedep as root-owned,
and event-log init then fail-closes every later non-root command.
Make that state self-solvable:
- event-log init permission errors exit with a usefulerror naming the
likely cause and the chown fix instead of a bare fatal
- pmg setup doctor probes event-log dir writability and reports the
same fix via a new per-result Fix override
- document the mechanism and remedy in system-install.md, along with
the binary ownership requirements for --system
- consolidate this branch's doctor tests into doctor_test.go
* fix: resolve per-user paths from root's own home when running as root
Path resolution trusted HOME (and XDG_*), which sudo and su can preserve
from the invoking user (GitHub runners, sudo -E, su without -). Any pmg
run as root then created root-owned ~/.config/safedep inside that user's
home, and event-log init fail-closed every later non-root pmg/npm/pip
run for them. System install made sudo pmg the documented flow, turning
this latent bug into the happy path.
When euid is 0, configDir and cacheDir now resolve from root's passwd
home instead of the environment, so root state lands under /root and
user homes are never touched. PMG_CONFIG_DIR/PMG_CACHE_DIR still win,
non-root resolution is unchanged, and Windows is unaffected (no euid).
Event-log init stays fatal on failure; sudo-run package events are
attributed via SUDO_USER and synced by the exit auto-sync as usual.
E2E: GitHub runners preserve HOME under sudo, so assert that no sudo
pmg run leaks state into the runner's home, and that the managed-config
refusal fails for the documented reason rather than a permission brick.
* fix: triage the unwritable config dir remedy by cause
The chown hint is only correct when another account created files
inside the current user's own home. When a leaked HOME or
XDG_CONFIG_HOME points at another user's home (e.g. sudo -u on GitHub
runners), following it would chown that user's directory and brick
their pmg instead. Classify the failure against the passwd home,
which the leaked environment cannot influence, and prescribe:
- dir inside own home: restore ownership with chown
- dir outside own home: fix the leaked environment, never chown
- explicit PMG_CONFIG_DIR: make it writable
Used by both the fatal event-log error and the doctor check, and the
docs troubleshooting now carries the same two-case triage.
* ci: pin XDG_CONFIG_HOME for the cross-user e2e step; terse doctor fix
GitHub runners export XDG_CONFIG_HOME=/home/runner/.config and it leaks
through sudo -u, so the pmgtest pmg resolved the runner user's config
dir and fail-closed on its runner-owned log file (run 29289868727 shows
the triaged error catching exactly this). Set it inside the login shell
so it wins regardless of how the leak is delivered.
The remedy now returns a full-help and doctor-table pair from a single
triage, and drops the do-not-chown tail from the leak message.
* fix: adapt event-log error to the two-value remedy signature
Belongs with the previous commit; it was left unstaged and
|
||
|
|
c47776db27 |
feat(proxy): persistent proxy server mode (#351)
* refactor(flows): extract SetupCACertificate for reuse Move the CA load/generate/merge logic out of proxyFlow into an exported flows.SetupCACertificate so the persistent proxy server can reuse it. * feat(proxy): add persistent proxy server with start/stop/env/status Introduces 'pmg proxy' commands backed by internal/proxyserver: a long-lived MITM proxy that intercepts package managers via env vars (no shims). Supports --daemon (Unix), --state, --port; generic 'env' output that skips cert vars when the CA is OS-trusted; opt-in 'stop --fail-on-violation' (fail-closed on crash) with a synchronous cloud event flush; and the malysis analysis cache. * feat(action): add server-mode for persistent proxy When server-mode=true the action starts the proxy daemon and injects proxy env vars into the job instead of installing shims. * test(proxy): add persistent proxy server E2E workflow * docs(readme): document persistent proxy server mode * fix(proxy): create cache dir before writing state file and daemon log On a fresh CI runner the cache directory does not exist yet; os.OpenFile and os.WriteFile do not create parent dirs, so 'pmg proxy start --daemon' failed with 'no such file or directory'. MkdirAll the parent before writing. * docs: add persistent proxy server architecture doc * refactor proxyserver * fix(proxy): always emit cert env vars instead of skipping on OS-trust status npm/pip/yarn/requests trust the MITM CA inconsistently across tools, versions, and configs; many still use bundled CA stores. Always emitting the cert-path env vars is the conservative choice that works regardless, and is harmless for tools that read the OS store (they ignore the vars). Skipping them when a system CA exists would silently break any tool still on a bundled store. * refactor(proxy): drop redundant audit init in daemon; rely on main.go main.go's PersistentPreRun already initializes the audit pipeline for every command (including the daemon's re-exec'd child) and closes it at process exit. Re-initializing in proxyserver.Run created a second auditor and a second cloud-sync WAL connection, orphaning the first. Removing it makes the daemon consistent with the normal proxy flow, which never self-initializes audit. * fix(proxy): bypass proxy env when flushing events to cloud on stop pmg proxy stop inherits HTTP(S)_PROXY (injected by 'pmg proxy env') pointing at the PMG proxy it just shut down. The cloud sync gRPC client honored those vars and routed api.safedep.io through the dead proxy, failing with 'connection refused' so no events were delivered. Clear the proxy env vars before the sync so PMG's own cloud traffic goes direct. * chore(proxy): address review feedback - configurable bind host via proxy.server.listen_host (default loopback) - proxy commands use ui.ErrorExit instead of returning errors to cobra - rename errcode to ProxyPolicyViolation (covers malware + cooldown) - share cloud sync via audit.DrainToCloud (de-dup with cmd/cloud/sync) - centralize proxy CA bundle path in certmanager - docs: persistent proxy cert trust + bind address * fix(proxy): show real message on fail-on-violation error stopExitError set only WithMsg, but ui.ErrorExit renders HumanError, so the framed error showed 'no human-readable message available'. Set both from one string, and emit the framed error before the stdout summary so the blocked count is stated once. * fix(proxy): flush cloud events from the daemon, not stop The stop process inherits HTTP_PROXY (from 'pmg proxy env'), so its cloud client routed api.safedep.io through the already-stopped proxy and failed with connection refused. Move the flush into the daemon's shutdown, which has no proxy env (it started before env injection) and dials SafeDep directly. - daemon flushes on shutdown via audit.DrainToCloud and records the result in the state file; stop surfaces it (on both success and fail-on-violation paths) since the daemon's own logs aren't visible to stop - coordinate stop's wait with the daemon shutdown budget; on timeout, error out without reading stale state or deleting the file (fail-closed) - persist blocked count before the flush so the gate stays correct if the flush hangs or the daemon is killed mid-flush - remove now-redundant cloud_flush.go * disable auto-sync for proxy cmds * feat(proxy): periodic cloud sync + move proxy env vars to packagemanager - daemon runs a periodic cloud-sync ticker so the shutdown flush stays small; the run total is reported by stop, and shutdown timeouts are coordinated - move EnvVarForProxy from config to packagemanager (it is package-manager knowledge); the shared function now builds the proxy URL and NO_PROXY itself, removing the duplicated construction in the per-command and persistent paths - relocate the #319 yarn and #339 IPv6 regression tests alongside the function - enable cloud sync in the persistent-proxy E2E workflow and fix the stale internal/proxystate path filter * refactor(proxy): rename cloudFlushLockTimeout to cloudFlushLockWait Consistent timeout naming: *LockWait is the lock-acquire bound, *Timeout is the sync-RPC bound. Previously the final-flush pair was cloudFlushLockTimeout vs cloudFlushTimeout — two lookalike names for different operations. * refactor(proxy): extract cloudFlush and trim duplicate shutdown comments The shutdown's final-flush block is now a cloudFlush helper, symmetric with startCloudSyncLoop (one-shot vs loop). Removed the triplicated ticker/lock contention comments, keeping the contract on the function doc and one-line pointers at the call sites. * docs: update persistent proxy cloud sync to daemon-owned model The daemon now owns cloud delivery (periodic sync while serving + final flush on shutdown); stop signals it, waits, and reports the result. Rewrite the Cloud event sync section, fix stop attributions, add the cloud_sync state field, and update the sequence diagram. * docs: move Usage section up below How it works Put the copy-paste recipes near the top so users find them before the internals. * refactor(proxy): address PR review feedback - configurable bind host/port via --host/--port flags + config (listen_host, listen_port), bound directly to config fields per PMG's flag pattern - daemon log path via --log-file and readiness timeout in ProxyDaemonConfig; Daemonize no longer owns path policy (caller validates, fails fast) - gate periodic cloud sync on auto_sync; suppress detached background sync for proxy commands instead of flipping the flag - pmg proxy env --export emits shell-quoted lines for eval (spaces survive) - extract shared flows.BuildCachedMalysisAnalyzer, dropping the analyzer+cache duplication between proxy flow and proxy server - add internal/proxyserver/doc.go documenting the package + boundary vs flows - E2E: assert malicious installs are blocked (drop continue-on-error) - docs: trim Commands/State-file to user contracts; refresh bind address * refactor(proxy): proactive alignment fixes from whole-PR review - gate the shutdown cloud flush on auto_sync too, matching the periodic ticker (auto_sync consistently controls all daemon-driven cloud delivery) - ResolveStatePath takes cacheDir instead of *RuntimeConfig, keeping state.go free of config dependency - drop the empty-host comment in listenAddr; keep the loopback guard so a blank host never silently binds all interfaces * fix: Decouple localdb with malysis analyser construction * fix: Persist global args before proxy server daemon exec * fix: GitHub Action for cloud auto-sync in server mode --------- Co-authored-by: Abhisek Datta <abhisek.datta@gmail.com> |
||
|
|
d360e75897 |
feat: Add malysis cache implementation with proxy flow integration (#346)
* feat: Add malysis cache implementation with proxy flow integration * fix: Code review fixes |
||
|
|
c7244f921a |
feat: Add support for environment protection (scrubbing) (#327)
* feat: Add support for environment variable protection for sandbox * chore: Update dangerous env var list * fix: Split profiles for improved environment protection * fix: pipx sandbox profile separation * chore: Show sandbox scrub info on error exit * fix: Code review fixes * test: Add e2e for sandbox environment scrubbing |
||
|
|
4f0db15ede |
feat: Add opt-in support for proxy CA cert installation (#318)
* feat: Add opt-in support for proxy CA cert installation * fix: Code review fixes * fix: Code review fixes * fix: Code review fixes * fix: Code review fixes * fix: Code review fixes * docs: Add limitation for MacOS MDM script |
||
|
|
083f82dd79 |
feat: Add pmg setup doctor command (#290)
* feat: add doctor check runner core types and logic * feat: add doctor checks for config, binary, directory, and aliases * feat: add doctor checks for sandbox and security features * feat: add protection verification check using test malicious packages * feat: add summarized package manager availability check * feat: add pmg setup doctor command with compact output * feat: add PATH shim verification to doctor command * fix: improve doctor command UX and alias detection - Capitalize all check messages for consistent output - Dim passing checks, color warn/fail for visual clarity - Silence empty Cobra error output on doctor failure - Remove redundant pmg binary check (self-evident) - Fix alias IsInstalled to skip commented-out source lines - Improve protection failure message * refactor: remove package manager availability check from doctor * fix: handle os.RemoveAll error in doctor protection check * refactor: use table layout for setup doctor, extract shared table renderer Move renderTable, truncate, and visibleWidth helpers from cmd/sandbox to internal/ui so both sandbox and setup doctor share them. Rewrite setup doctor output to use the same table structure as sandbox doctor. Fix VisibleWidth to count runes instead of bytes for correct alignment with multi-byte UTF-8 characters. * docs: add pmg setup doctor to README, remove manual verification step * refactor: use constants for check names, rename and inline doctor helpers Address PR review comments: extract check name constants, rename CheckConfigFile to CheckFileExists and CheckDirectoryWritable to CheckDirectoryExists for reusability, inline trivial wrappers (CheckSandbox, CheckSecurityFeature, CheckProxyMode), and add fix hints for all checks with correct config keys. * refactor: inline simple doctor checks into command layer * fix: skip protection check when aliases and shims are inactive Protection checks now fail immediately when shell aliases and shims are both inactive, instead of falsely passing by running through the pmg binary directly. Also clean up summary messages to remove redundant fix hints and truncated paths. |
||
|
|
0e3bb52f8c |
fix: Opt-in lockdown for global config (#278)
* fix: Reject overriding managed flags * fix: Lockdown overrides when global config present * fix: Opt-in lock-down enforcement for global config * fix: Code review fixes * fix: Code review fixes * fix: Code review fixes |
||
|
|
b15ce33fe4 |
fix: MacOS MDM based Deployment (#277)
* fix: MacOS MDM deployment script * fix: Handle shell alias for bash on macos * fix: Code review fixes * fix: Code review fixes * feat: Add support for global config file * feat: Add support for global config file * fix: Code review fixes * fix: Avoid blocking CLI for analytics flush |
||
|
|
9b0e12f130 |
feat: Add Support for Optimistic Cloud Sync (#273)
* feat: Add support for background sync * refactor: Maintain single source of truth for command defn * fix: Code review fixes * fix: Code review fixes * docs: Add corner case inline doc |
||
|
|
b8588e3df4 |
feat: Add Sandbox Inspection and Debugging Commands (#261)
* feat: add sandbox DX commands * fix: Linter errors * fix: Sandbox deny log parsing * fix: Sandbox docs * refactor: Maintain SSOT across pkg dependencies * fix: Linter errors |
||
|
|
c78287e5a0 | feat: add pmg config get/set/edit CLI commands (#262) | ||
|
|
6546116e28 |
feat: migrate PMG to use PATH shims for package manager wrapper (#246)
* feat: add FilterPMGFromPath utility for PATH shim recursion prevention * feat: add FilterPMGFromEnv to filter PATH from env slices * feat: filter ~/.pmg/bin from PATH in proxy subprocess env * feat: add PathExport method to Shell interface for shim PATH integration * feat: add ShimManager for PATH shim install/remove lifecycle * feat: wire ShimManager into setup commands with --use-aliases fallback * refactor: add DefaultShimConfig helper to reduce setup boilerplate * fix: resolve real binary path to prevent shim double-invocation exec.CommandContext resolves the binary using the current process PATH, which still contains ~/.pmg/bin. This caused pmg to launch the shim instead of the real package manager, resulting in a second pmg instance with its own proxy — producing duplicate error messages and wasted work. ResolveRealBinary searches a filtered PATH (without ~/.pmg/bin) to find the real package manager binary before execution. * fix: resolve real binary in runner.Execute and expand path resolution tests Ensure guard mode and proxy skip paths also resolve through ResolveRealBinary to prevent infinite shim recursion. Add table-driven tests covering error cases, multi-binary PATH, and PATH restoration. * fix: handle error return values from os.Setenv and file Close calls Address errcheck lint failures: check os.Setenv returns in ResolveRealBinary, and check f.Close/tempFile.Close in ShimManager. * feat: auto-migrate shell aliases to PATH shims on setup install When running `pmg setup install`, detect existing shell aliases and automatically remove them before installing shims. Existing users get a seamless migration with no extra flags or commands needed. * fix: update E2E test to verify shim installation instead of alias RC file Replace the .pmg.rc file check with assertions that ~/.pmg/bin/ exists and contains executable shim scripts for npm and pip. * feat: add FilterPMGFromPath utility for PATH shim recursion prevention * feat: add FilterPMGFromEnv to filter PATH from env slices * feat: filter ~/.pmg/bin from PATH in proxy subprocess env * feat: add PathExport method to Shell interface for shim PATH integration * feat: add ShimManager for PATH shim install/remove lifecycle * feat: wire ShimManager into setup commands with --use-aliases fallback * refactor: add DefaultShimConfig helper to reduce setup boilerplate * fix: resolve real binary path to prevent shim double-invocation exec.CommandContext resolves the binary using the current process PATH, which still contains ~/.pmg/bin. This caused pmg to launch the shim instead of the real package manager, resulting in a second pmg instance with its own proxy — producing duplicate error messages and wasted work. ResolveRealBinary searches a filtered PATH (without ~/.pmg/bin) to find the real package manager binary before execution. * fix: resolve real binary in runner.Execute and expand path resolution tests Ensure guard mode and proxy skip paths also resolve through ResolveRealBinary to prevent infinite shim recursion. Add table-driven tests covering error cases, multi-binary PATH, and PATH restoration. * fix: handle error return values from os.Setenv and file Close calls Address errcheck lint failures: check os.Setenv returns in ResolveRealBinary, and check f.Close/tempFile.Close in ShimManager. * feat: auto-migrate shell aliases to PATH shims on setup install When running `pmg setup install`, detect existing shell aliases and automatically remove them before installing shims. Existing users get a seamless migration with no extra flags or commands needed. * fix: update E2E test to verify shim installation instead of alias RC file Replace the .pmg.rc file check with assertions that ~/.pmg/bin/ exists and contains executable shim scripts for npm and pip. * feat: install both aliases and shims for full coverage Aliases win in interactive shells (including venvs), shims catch non-interactive contexts (IDEs, CI, subprocesses). Remove --use-aliases flag and migration logic since both are always installed together. Update E2E to verify all shim scripts and alias RC file. * feat: address review feedback for shim implementation - Install both aliases and shims together for full coverage - Move homeDir resolution into NewDefaultShimManager (internal concern) - Add mutex to ResolveRealBinary to guard against concurrent PATH mutation - Use filepath.SplitList for platform-correct PATH splitting - Add ResolveRealBinary to runner.Execute and proxy flow to prevent shim recursion in all execution paths - Remove print side-effects from ShimManager.Remove - Update E2E to verify all shim scripts and alias RC file - Expand ResolveRealBinary tests with table-driven cases * fix: restore errcheck handling and add concurrency test for ResolveRealBinary - Restore proper defer with log.Warnf for PATH restoration in ResolveRealBinary - Restore errcheck handling for f.Close() and tempFile.Close() in ShimManager - Add explanatory comment for ResolveRealBinary call in proxy_flow - Add TestResolveRealBinaryConcurrent to verify mutex guards concurrent access * feat: skip shell integration on Windows with informative warning On Windows, pmg setup install now writes only the config file and prints a warning that shell aliases and PATH shims require WSL. * fix: PMG use pre-resolved binary path (#253) --------- Co-authored-by: Abhisek Datta <abhisek.datta@gmail.com> |
||
|
|
a31c201117 |
chore: Add a landlock test for path deny (#247)
* chore: Add a landlock test for path deny * fix: Make sure sandbox is available: |
||
|
|
d1dd2560a4 |
feat: consolidate proxy config into structured section and add support for custom commands to skip proxy (#240)
* feat: add ProxyConfig struct with per-PM skip_commands and legacy fallback * feat: consolidate proxy config into structured section with backward compat Replaces flat proxy_mode/proxy_install_only keys with a structured proxy section supporting per-package-manager skip_commands. Legacy keys are respected via fallback when user's config lacks the new proxy section. Removes deprecated experimental_proxy_mode config and flag. * fix: env var resolution for nested config keys and deduplicate skip command matching - Add "." to "_" in Viper env key replacer so nested keys like sandbox.enabled resolve from PMG_SANDBOX_ENABLED (was silently broken) - Export IsFirstNonFlagArgInList and remove duplicate from proxy_flow.go - Add table-driven tests for skip command matching with real-world cases - Remove redundant env var test * docs: update proxy configuration and env var documentation Update config.md env var table to reflect new proxy.enabled and proxy.install_only keys. Add proxy configuration section to proxy.md covering config structure, per-PM skip commands, CLI flags, and env vars. * fix: legacy fallback precedence |
||
|
|
b56a8e2a43 |
chore: Show cloud credentials store in setup info (#236)
* chore: Show cloud credentials store in setup info * fix: pnpm sandbox policy for atomic write |
||
|
|
27551d5019 |
feat: Add pmg setup edit command (#235)
* feat: Add pmg setup edit command * fix: Code review fixes |
||
|
|
996d9aeca0 |
Add config support for disabling telemetry (#226)
* Add config-driven telemetry disable and surface telemetry status * Update config/config_template_test.go Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> Signed-off-by: Sahil Bansal <bansalsahil315@gmail.com> --------- Signed-off-by: Sahil Bansal <bansalsahil315@gmail.com> Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> |
||
|
|
e67735c1c3 |
feat: Add cloud sync event emit (#212)
* feat: Add cloud sync event emit * fix: Linter fixes * fix: Include malysis metadata in confirmed event * fix: Code review fixes * fix: Emit session complet event * fix: Code review fixes * chore: Add comment * chore: Add cloud info in setup info command * fix: Proxy flow must call install started * fix: Code review fixes * fix: Code review fixes * Update internal/audit/cloud_sink.go Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com> * fix: Code review fixes --------- Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com> Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> |
||
|
|
987bda5d6a |
feat: Add dependency cooldown for npm packages (#200)
* feat: Add dependency cooldown for npm packages Strip recently-published package versions from npm registry metadata responses so npm's resolver naturally falls back to older versions. Overrides the Accept header to force full packument responses (which include the "time" field needed for publish-date checks). Reports cooldown blocks only when all versions are stripped (remaining == 0), matching npm's --min-release-age behavior for silent fallback. * fix: Report oldest version in cooldown block (shortest wait) When all versions are blocked by cooldown, report the oldest version since it exits the cooldown window first — giving the user the shortest wait time instead of the longest. * fix: Handle resp.Body.Close error return for errcheck linter * test: Add dependency cooldown assertions to template config tests * fix: config template for dependency cooldown * fix: Prevent npm from caching cooldown-stripped metadata responses * fix: Restore body on ReadAll failure and log Close errors in response modifier * fix: Close response body before replacing to prevent connection leak * fix: Correct daysLeft ceiling math and update ContentLength on error recovery * fix: Clear Status on status code change and update ContentLength in modifier error path * refactor: address review comments on dependency cooldown PR - Make NpmCooldownHandler and constructor package-private - Pass cooldown days as parameter instead of reading config internally - Convert standalone functions to methods on npmCooldownHandler - Set Accept-Encoding: identity to prevent gzip responses breaking JSON parsing - Return 503 with descriptive message when upstream body read fails * fix: log errors in stripCooldownVersions instead of swallowing them * fix: Config preserve fallback defaults * fix: Code review fixes * fix: correct cooldown tip to show wait time instead of incorrect trusted_packages advice * fix: prevent integer overflow in cooldown duration calculation with large days values * refactor: deduplicate CooldownBlock into internal/models, fix misleading variable names - Move CooldownBlock struct to internal/models to eliminate duplication between proxy/interceptors and internal/ui packages - Simplify proxy_flow.go by using direct assignment instead of field copy - Rename latestStripped/latestDate to oldestVer/oldestDate for clarity * fix: Dependency Cooldown Check Encapsulation (#207) * fix: Encapsulate cooldown check * feat: Add --skip-dependency-cooldown override * fix: Code review fixes --------- Co-authored-by: Abhisek Datta <abhisek.datta@gmail.com> |
||
|
|
d112ded3da |
feat: Merge template config into existing user config during setup install (#189)
* 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> |
||
|
|
91aa4547c4 | feat: Show sandbox info in PMG setup info command (#170) | ||
|
|
28c7b6c843 |
Make proxy mode default for npm based managers (#148)
* update npm pkg managers to use proxy mode as default * update config template for default to true for proxy_mode * update e2e for proxy mode * update info cmd for correct proxy mode status * Update config/config.template.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Sahil Bansal <bansalsahil315@gmail.com> * Update config/config.template.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Sahil Bansal <bansalsahil315@gmail.com> * Update config/config.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Sahil Bansal <bansalsahil315@gmail.com> --------- Signed-off-by: Sahil Bansal <bansalsahil315@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> |
||
|
|
b332e1d6d4 |
update setup install cmd info (#143)
* update setup install cmd info * update demo * add doc comment |
||
|
|
c0122898ca |
feat: Add support for setup-info command (#108)
* feat: Add support for setup-info command * fix: Code review fixes * fix: Add event log info |
||
|
|
20c854e473 |
feat: Config Persistence & API (#83)
* introduce a persistent config * add tests and refactor config creation * update config handling and add support for removing config * add support to skip suspicious pkgs marked as trusted * add support for config dir Env & unexport functions * small fixes * add assert for dir * fix tests * fix shell source line & trusted pkgs parsing * fix flag inconsistency * update config to read on each invocation and create if does not exist * fix flags value being overridden * remove redundant func call * modify trusted pkg check to be config bound * modify RemoveConfig to rm files & not dir. add tests for paths.go * add versions for package for e2e * modify tests to reset config * fix: Simplify config persistence * fix: Misc comments * fix: Misc fix * fix: Do not overwrite config file if exists * fix: Do not overwrite config file if exists * fix: Config cobra command should override and not replace * fix: Create dir before writing config template * fix: Create dir before writing config template * fix: Misc refactoring * test: Add test for is trusted package version * Update cmd/setup/setup.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com> * Update config/config.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com> * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com> * fix: Remove unused constant in config * fix: Resolve conflict with event logger * docs: Add doc for eventlogger.Logger interface * test: Add E2E for config file creation * fix: Code review fixes --------- Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com> Co-authored-by: Sahilb315 <bansalsahil315@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> |
||
|
|
cd8fb52c47 |
feat: new pmg banner (#68)
* feat: new pmg banner * commit lenght check |
||
|
|
2493fb4dc6 | chore: Better user friendly error messages (#64) | ||
|
|
d363167981 |
feat: add setup & remove cmd for better UX (#43)
* feat: add setup & remove cmd for better UX * refactor(alias): improve separation of concerns and fix shell sourcing * chore: remove extra/unused folder * refactor: introduce separate files for shells * fix: use temp files for safe shell config modification |