mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
77cf263ca0ac7a68c954681f97bccbb53191efcd
41
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. |
||
|
|
ee684a29a9 |
feat(sandbox): presets — additive workload allowance bundles (#387)
* feat(sandbox): introduce presets - additive workload allowance bundles Presets are named, additive-only bundles of sandbox allowances for a specific workload (git hooks tooling, Astro/Vite/Next.js dev servers). They solve the per-workload tuning friction from #384 without weakening the default posture: no built-in profile references a preset, presets cannot carry deny rules or profile booleans (strict YAML decoding), and mandatory denies still win everywhere except the existing exact-match suppression. - Preset schema with metadata (author, labels) and schema_version gating - Registry over ordered sources (embedded builtin, user dir); builtin wins name collisions; source abstraction is the extension point for a future hosted registry and SafeDep cloud sync - Official presets: git, astro, vite, nextjs (with threat notes) - Overlay/runtime integration: pmg sandbox allow preset=<name> and --sandbox-allow preset=<name>, stored by reference, resolved at apply time, missing presets warn (fail closed) instead of aborting - Profile integration: presets: [...] list resolved after inherits - CLI: pmg sandbox preset list (metadata filters, --json), show (prints YAML with threat notes), lint - Docs: user guide (docs/sandbox-presets.md) and design spec Closes #384 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PETCfE4crLcmodosz12qRn * fix(sandbox): address review findings on presets - Presets never modify deny lists: a profile authored deny now survives a preset allowing the same path (deny-beats-allow keeps it enforced). Regression test added. - Profile inspection commands (show, diff, lint) construct the profile registry with the user-aware preset registry so they agree with runtime resolution of custom profiles referencing user presets. - Handle stderr write error when warning about unresolvable presets. - Compute preset show underline from the uncolored header. - Use path.Join for embed.FS reads (slash-separated on all platforms). - Clarify in docs that lint-staged/astro are examples of preset workloads. - Drop the design spec from the PR per review. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PETCfE4crLcmodosz12qRn * fix(sandbox): harden preset precedence against authored denies Addresses external security review findings on the preset mechanism: - Bubblewrap: a mandatory write-denied path listed in allow_read lost its protection when a later writable parent bind covered it (bwrap last mount wins) - exactly the git preset shape (allow_read .git/config + allow_write .git/**). The mandatory deny now re-binds the path read-only after all writable mounts instead of being skipped. Regression test asserts mount ordering. Landlock and Seatbelt were unaffected (tests added for the same policy shape on Landlock). - Environment: ScrubEnv is allow-wins, so a preset environment allowance could override a profile-authored deny. Preset env allowances overlapping an authored deny pattern are now dropped at application time (conservative bidirectional glob overlap, fail closed). Surviving entries still opt out of built-in credential scrubbing as intended. - Network: removed allow_outbound from the preset schema. Both platform translators are all-or-nothing for outbound (one allow rule means blanket network access), so a preset outbound entry would silently change network posture far beyond what its YAML conveys. Strict decoding rejects the key. - Added a dual-path expansion equivalence test (profile presets: field vs overlay/--sandbox-allow) and documented the precedence guarantees in docs/sandbox-presets.md. Explicit --sandbox-allow and pmg sandbox allow overrides keep their existing semantics. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PETCfE4crLcmodosz12qRn * docs(sandbox): document env and preset allowances in allow command and overlay docs pmg sandbox allow help, the --sandbox-allow flag usage, and the project overlay docs enumerated only read/write/exec/net types. Add env and preset to all of them, with an overlay example for persisting an env allowance and a note on why env entries are not auto-promoted by --last. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PETCfE4crLcmodosz12qRn * chore(sandbox): trim preset code comments to corner cases and minimal godocs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PETCfE4crLcmodosz12qRn * fix(sandbox): exact glob intersection for preset env deny overlap The bidirectional literal-text heuristic missed overlapping globs with different literal structure: preset allow AWS_*_KEY and authored deny AWS_SECRET_* both match AWS_SECRET_ACCESS_KEY but neither pattern matches the other's text, so the allowance merged and allow-wins scrubbing exposed the variable. EnvPatternsOverlap now computes exact intersection non-emptiness for the name glob dialect (case-insensitive, '*' any sequence, '?' single char) via memoized DP. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PETCfE4crLcmodosz12qRn * fix(sandbox): preset env allowances are exact names, not globs Glob-vs-glob intersection is a losing game: every dialect extension (character classes today) silently reopens the deny-bypass hole. Restricting preset environment allowances to literal variable names makes the authored-deny precedence check exact by construction: each deny pattern is evaluated against the concrete name with the same matcher ScrubEnv uses at runtime, so the decision cannot diverge from enforcement regardless of deny dialect. Removes the glob intersection machinery. Profile and --sandbox-allow env globs are unaffected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PETCfE4crLcmodosz12qRn * fix(sandbox): reject mandatory-deny targets in preset paths Preset validation relied on IsSensitiveProjectTarget, which covers fewer files than util.DANGEROUS_FILES. A preset naming .git-credentials, .pgpass, .docker/config.json or .config/gh exactly would exact-match suppress the mandatory deny; .git/config in allow_write would suppress the write protection. Preset paths are now checked against DANGEROUS_FILES (single source of truth), .git/hooks is rejected in any direction, and .git/config is rejected for write/exec while read stays allowed for git repo discovery. Docs state the two deliberate opt-outs precisely. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PETCfE4crLcmodosz12qRn * feat(sandbox): preset init and edit commands for community authoring pmg sandbox preset init scaffolds a valid user preset (metadata flags, threat-note template, starter rule) and refuses built-in names since builtins win resolution. pmg sandbox preset edit opens the file via the shared editor package and validates the result, warning when a user preset is shadowed by a built-in. Docs lead with the scaffolded flow and spell out builtin-vs-community provenance in preset list. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PETCfE4crLcmodosz12qRn * refactor(sandbox): move mandatory-target matching into util Preset path validation re-encoded knowledge util already owns: the dangerous-files comparison and hardcoded .git/config and .git/hooks strings. util now exports GitConfigPath, GitHooksPath (also used by GetMandatoryDenyPatterns), PathCoveredBy and DangerousFileMatch, and preset validation consumes them so the mandatory deny policy has a single definition. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PETCfE4crLcmodosz12qRn --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
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
|
||
|
|
31e51b10dd |
refactor: move proxy block message rendering to presentation layer (#364)
* refactor: move proxy block message rendering to presentation layer * refactor: introduce ui.ProxyPresenter with injected advisory source * feat: friendly ecosystem labels in proxy block messages * test: cover ecosystemLabel derivation * fix: address review feedback on block context assertions and empty reference line |
||
|
|
2d938ea381 |
feat: advisory message appended to block output (#362)
* docs(specs): add custom block messages and package blocklist spec * docs(specs): add custom block messages and package blocklist implementation plan * feat(config): add blocked_packages list and custom block messages * feat(audit): add package_blocklist_blocked event and blocklist model * feat(proxy): block blocklisted packages in the policy gate before analysis * feat(guard): block blocklisted packages before trust skip and analysis * feat(ui): render blocklist blocks and custom messages, fix silent-mode block output * feat(proxy): append custom messages to malware and go-cooldown block bodies * test(proxye2e): cover blocklist enforcement and custom block messages * docs(specs): remove spec and plan documents * refactor: drop guard-flow blocklist enforcement and trim docs Guard mode is being deprecated; the blocklist is enforced in proxy mode only. Remove the trusted_packages mirroring references outside the docs. * refactor(config): consolidate blocklist and block message under top-level block section Replace dependency_cooldown.message, malware.message and blocked_packages with a single block section: block.message is appended to every block output regardless of which control blocked, and block.packages is the package blocklist. * fix(ui): render block.message as info note with clean spacing * fix(ui): indent wrapped continuation lines in block reasons and messages * update config template * refactor(config): replace block section with top-level advisory_message Remove the package blocklist (will be implemented as part of policies in the future) and replace block.message with an optional top-level advisory_message appended to every block output. * chore(config): move advisory_message near top-level scalar configs in template |
||
|
|
648adcbda4 |
feat: add uvx (uv tool run) package executor (#357)
* feat(uvx): add uvx (uv tool run) package executor Adds support for `uvx`, implemented as a PyPI Executor alongside pipx. uvx is an alias for `uv tool run`: it installs a tool into an ephemeral environment and runs it, so it has no install/list subcommand and the first positional argument (or --from) is the package to audit. Parsing highlights: - --from overrides the positional command as the package to audit - --with packages are audited as additional environment dependencies - name@version shorthand (ruff@0.3.0, ruff@latest) is normalized - flag parsing stops at the tool name so the tool's own flags are not misread as uvx options; uvx's value/boolean flags are registered so none greedily consume the package positional - VCS/URL/local-path specs are skipped for registry auditing Wires up command registration, analytics, shell alias/shim, cloud audit mapping, a dedicated `uvx` sandbox profile (UV_*/PIP_* env, uv cache and tool dirs), config policy, docs, unit tests and an E2E workflow step. Closes #326 https://claude.ai/code/session_011hyLxq7oWJX5Dp4tCEfG19 * chore(uvx): align docs and base profile with uvx support Incorporates the low-risk, non-parser improvements from the community PR #345 (author non-responsive) into our implementation: - list uvx (and the previously-missing pipx) as PyPI managers in the pypi-restrictive base profile package_managers and its README, so the base profile applies directly when selected via --sandbox-profile - document uvx in docs/github-action.md and docs/proxy-mode.md - add version / IsExplicitVersion assertions to the uvx parser tests Our pflag-based parser is kept as-is: unlike #345 it audits --with packages and handles all uvx short flags (e.g. -w), both of which the community PR misses. * fix(uvx): skip interpreter requests; use require in tests Addresses review feedback on PR #357: - uvx interpreter requests (`uvx python`, `uvx python@3.12`, `uvx pypy`, ...) launch an isolated interpreter rather than installing a PyPI tool. Treating the positional as a package made the guard flow resolve/analyze pkg:pypi/python (and python==3.12), which could wrongly block or fail a valid invocation. Skip these for the positional; --with packages on the same command are still audited. - Use require.NoError / require.Len for fatal assertions in the uvx tests, matching the repo's testing convention, so a failure stops the subtest before a nil dereference instead of panicking. * docs(uvx): document fail-open and --with-requirements trade-offs Record the two deliberate parsing decisions raised in review as in-code trade-off comments (no behavior change): - unknown flags are tolerated (fail open), consistent with the other executors; the residual gap only affects non-proxy guard mode since the default proxy flow intercepts every registry download. - --with-requirements / --with-editable values are consumed but not expanded into audit targets; expanding them needs manifest-extractor and guard changes, tracked as follow-up. Proxy mode still covers them. * docs(uvx): drop --with-requirements limitation note Per maintainer review: guard mode is being deprecated and auditing the contents of an existing requirements file is a scanner's responsibility, not PMG's. Remove the "known limitation / follow-up" note; the flags stay registered only so their values are not mistaken for the tool positional. --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
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 |
||
|
|
327c9c7068 |
feat(cooldown): respect trusted_packages in dependency cooldown (#342)
* feat(cooldown): respect trusted_packages in dependency cooldown Trusted packages are now treated as a superset waiver that bypasses every PMG control (malware analysis, cooldown, and any future controls). A globally trusted package is automatically exempt from the cooldown window and no longer needs a duplicate entry in dependency_cooldown.skip. The skip list remains the narrower, cooldown-only waiver for packages that must bypass the cooldown wait but still be malware-scanned. * refactor(cooldown): tag skip reason and audit-log skipped packages Address review feedback on #342: - Restore cooldownSkip to a pure single-list function (SRP); the merge into trusted_packages now happens in a separate mergeCooldownSkip step, driven by the exported CooldownSkip wrapper. - Extend CooldownSkipInfo with a CooldownSkipReason (TrustedPackage / CooldownSkipList) on both SkipAll and per-version entries, so callers can tell apart the broad waiver from the cooldown-only one. When both lists match the same package, trusted_packages wins. - Add audit.LogCooldownSkipped and emit it from the npm and PyPI interceptors on the SkipAll path, alongside the existing info log, carrying the source list as the reason. * refactor(cooldown): inline list merge, audit per-version exemptions Address further review feedback: - Drop the separate mergeCooldownSkip helper; cooldownSkip now writes into a shared *CooldownSkipInfo and is called twice from CooldownSkip (cooldown skip list first, trusted_packages on top so trusted entries override the reason on overlap). - Audit log every exemption, not just SkipAll: a new auditCooldownSkip helper in proxy/interceptors/cooldown.go emits one event per match (package-wide or per-version), each tagged with its source list. LogCooldownSkipped gains a version argument for the per-version case. - Cover the trusted_packages reason path in TestCooldownSkip. * fix(cooldown): avoid double-auditing trusted package exemptions auditCooldownSkip now only emits EventTypeCooldownSkipped for entries that came from dependency_cooldown.skip. Trusted-package exemptions already get an EventTypeInstallTrustedAllowed event at tarball-download time (proxy/interceptors/base_registry.go), so emitting a cooldown event for them too would double-count the same waiver. * emit trusted and cooldown skip events to cloud * fix tests * refactor(cooldown): return value from collectCooldownSkip, short-circuit on trusted SkipAll Address PR review feedback: - Rename cooldownSkip to collectCooldownSkip and return CooldownSkipInfo instead of mutating an input pointer. - Add mergeCooldownSkip to combine per-list results with trusted_packages taking precedence on overlap. - CooldownSkip now consults trusted_packages first and returns immediately on a package-wide trusted exemption (DC skip list cannot add anything). - Extend tests to cover disjoint pinned entries across both lists and the case where DC version-less subsumes a trusted pinned entry. * fix(audit): address cooldown review feedback * fix(cooldown): audit cooldown skips at download time with concrete version Backend rejects PackageVersion messages without a version, and audit logs should reflect the runtime fact (a specific version was skipped) rather than the config rule. Move the audit emission from metadata-request handling to download-request handling, where the concrete version is known, and require version in LogCooldownSkipped. * chore(audit): drop dead scope assignment in LogCooldownSkipped * refactor(cooldown): move skip-list logic into cooldown handlers Registry interceptors no longer compute CooldownSkip or branch on SkipAll; they just call HandleMetadataRequest. The npm and pypi cooldown handlers own the skip lookup, the package-wide exemption short-circuit, and (for pypi) the canonical-name denormalization. Also align LogCooldownSkipped with other LogXxx signatures by taking *packagev1.PackageVersion. * fix: Simplify audit logging for dependency cooldown skip * refactor: Simplify cooldown handling and maintain separation of concepts for trusted and DC skip packages * fix: Code review fixes * fix: Emit cooldown skipped audit event ONLY when an in-window version is skipped --------- Co-authored-by: Abhisek Datta <abhisek.datta@gmail.com> |
||
|
|
baf637be97 | feat(analyzer): analysis cache contract (MalysisCache interface + config) (#334) | ||
|
|
61230fbcd7 |
feat(cooldown): add dependency_cooldown.skip list (per-control exemption) (#328)
Let dependency cooldown respect an explicit skip list so first-party / internal packages that must be installed the moment they are published (e.g. to sanity-test a freshly released version) are not held back by the cooldown window. Per review, this is a per-control skip list — NOT a second definition of "trusted package". There remains a single top-level `trusted_packages` (which waives malware analysis); `dependency_cooldown.skip` waives ONLY the cooldown wait, so a fast-tracked package is still malware-scanned. Matching: - a PURL without a version skips cooldown for all versions of the package (package-level) — the metadata passes through unmodified; - a PURL with a version skips cooldown for that version only — that version is preserved during stripping while other recent versions are still held. - config: DependencyCooldownConfig.Skip + CooldownSkip()/CooldownSkipInfo. - npm/pypi interceptors: bypass on package-level skip; thread per-version exemptions into the cooldown stripper so pinned versions survive. - docs + config template; unit tests for the matcher (package/version level, precedence, mismatches) and the skip-vs-trusted independence. Signed-off-by: dmdhrumilmistry <56185972+dmdhrumilmistry@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
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 |
||
|
|
b03e82e3f2 |
feat: Add support for per-project sandbox overlays (#294)
* feat: Add support for per-project sandbox overlays * chore: Maintain consistency with TUI experience * fix: Code review fixes |
||
|
|
219d743b80 |
chore: Standardise Error Codes (#286)
* fix: Misc error handling fixes * fix: Sandbox error translation |
||
|
|
20e01d5cae |
refactor : Refactor error handling to use dry/usefulerror (#283)
* update the go.sum Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com> * migrate most of the files to dry errors Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com> * update the rest of the files Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com> * fixs the review comments Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com> * chores Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com> --------- Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com> Co-authored-by: Abhisek Datta <abhisek.datta@gmail.com> |
||
|
|
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) | ||
|
|
d8abfb6c41 |
fix: flatten proxy skip_commands schema and move docs to proxy-mode.md (#241)
- Replace nested policies map with flat skip_commands map in ProxyConfig - Make skip_commands dependent on install_only being enabled - Move proxy configuration docs from proxy.md to proxy-mode.md - Update config template and tests for new schema |
||
|
|
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 |
||
|
|
1d9045d770 |
fix: Enforce sandbox for update commands that may download packages (#220) (#229)
ConfigureSandbox was only triggered by IsInstallationCommand(), missing update commands (npm update, pnpm update, etc.) that pull new versions and run postinstall scripts. Use MayDownloadPackages() as the sandbox signal so all package-downloading commands are sandboxed. Co-authored-by: Abhisek Datta <abhisek.datta@gmail.com> |
||
|
|
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> |
||
|
|
365deb1897 |
feat: Add proxy_install_only config to restrict proxy to download commands (#222)
* 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> |
||
|
|
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> |
||
|
|
8cba52201c |
feat: Add config support for UI verbosity level (#175)
* feat: Add config support for UI verbosity level * docs: Add verbosity info in UI doc |
||
|
|
6074080219 |
feat: Add support for sandbox allow override (#165)
* feat: Add support for sandbox allow override * fix: Main should fail on arg processing error * fix: Remove redundant policy conflict check |
||
|
|
41e3b9b22e |
Deprecate malysis active scanner (#155)
* deprecate malysis active scanner * Update config/config.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Sahil Bansal <bansalsahil315@gmail.com> * copilot fixes --------- Signed-off-by: Sahil Bansal <bansalsahil315@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> |
||
|
|
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> |
||
|
|
b5696f989f |
chore: Proxy mode without experimental tag (#147)
* chore: Proxy mode without experimental tag * fix: Update proxy mode docs * fix: Code review fixes |
||
|
|
736c63a7b6 |
Sandbox fails for unsupported cmds (#129)
* introduce enforce_always sandbox config & hooks for flows * fix sandbox failing for unsupported commands * add hooks for pypi package managers * add tests for sandbox hook * introduce enforce_always flag for ease use & CI * make comments descriptive * remove hooks & update config to add API to configure sandbox * add comments * rm unused function |
||
|
|
9693428171 |
feat: Experimental Sandbox Support (#101)
* feat: Sandbox implementation with seatbelt * refactor: Remove concept of PM_CACHE * fix: Misc fixes * refactor: Sandbox for separation of boundaries * fix: Apply API * fix: Add support for sandbox cleanup * test: Add variable interpolation test * fix: Misc cleanup fixes * chore: Cleanup sandbox registry * chore: Cleanup sandbox policy * chore: Cleanup sandbox * fix: Misc cleanup fixes * fix: Remove violation mode * fix: Update config template * chore: Go mod cleanup * fix: Handle the case when package manager policy is explicitly disabled * fix: Sandbox executor * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com> * test: Remove unused var * test: Add test for seatbelt sandbox driver * fix: Sandbox profile loader from file should use path for caching * test: Add policy test * feat: Add support for config templates * fix: Seatbelt translator handle glob * fix: Merge conflicts * fix: Fix sandbox policy generator for MacOS min permissions * fix: Sandbox path handling bugs * fix: Deny read to dangerous directories * fix: Deny read to dangerous directories * add sandbox e2e (#112) * fix: Sandbox E2E test * fix: Code review fixes * fix: Code review fixes * doc: Add sandbox debugging guide * doc: Update sandbox doc * docs: Add sandbox usage doc * fix: Use better error for sandbox without policy * fix: Add sandbox for npx * fix: Enable PTY for npm --------- Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Sahil Bansal <bansalsahil315@gmail.com> |
||
|
|
1c319eba0e |
fix: Proxy flow should respect trusted packages (#96)
* fix: Handle trusted packages in proxy flow * perf: Pre-parse trusted PURLs * fix: Code review fixes * fix: Remove unused config |
||
|
|
779deeb23d |
feat: Add Support for Proxy Based Npm Interceptor (#87)
* 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> |
||
|
|
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> |
||
|
|
ca752edf79 |
feat: add suppport for bypassing the blocking behavior of malicious packages (#53)
* feat: add suppport for bypassing the blocking behavior of malicious packages * feat: add InsecureInstallation config to bypass malware scanning with tests * ui: introduce ShowWarning interaction method * guard test fix |
||
|
|
b85f77cfbc |
feat: Add support for active scanning in paranoid mode (#31)
* feat: Add support for active scanning in paranoid mode * docs: Fix README |
||
|
|
e86b6ef056 |
feat: Refactor PMG to Maintain Separation of Concerns and Clean Architecture (#19)
* feat: Add separate package manager and resolver * fix: Npm dependency resolver * feat: Add analyzer for malysis query * feat: Add package manager guard as the orchestrator * feat: Add PMG to orchestrate installation * Add concurrent scan execution * Introduce package manager interaction abstraction * feat: Add UI port for guard * Remove refactored source files * Update README * fix: CI script for multi-arch build * ci: goreleaser CI fix * fix: npm command parser to extract package names * feat: Introduce global config primitive * fix: Close results channel for clean goroutine exit * ci: Add container image releaser * test: Improve test for npm resolver * refactor: Analyzer to generalise * Improve UI with additional info * fix: Goreleaser config * fix: npm resolver bug * fix: Fail when command exec workflow fails * fix: Bug with transitive dependency resolution * fix: Synchronize common data update in dependency resolver * chore: Improve log handling * docs: Update README * fix: UI text wrapping * fix: UI handling bugs * feat: Use concurrent dependency resolver |