14 Commits
Author SHA1 Message Date
Sahil BansalandGitHub 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
2026-07-10 17:42:26 +05:30
Sahil BansalandGitHub 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
2026-07-09 17:49:00 +05:30
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>
2026-06-21 18:22:15 +05:30
c3f3920d2e fix(proxy): harden MITM proxy reliability and scale for bulk installs (#314)
* fix(proxy): harden MITM proxy reliability and scale for bulk installs

Deep-dive analysis of dropped connections during large installs (5000+
packages with concurrent downloads) surfaced three issues, each verified
with a reproduction test before fixing.

1. Transient upstream errors tore down whole keep-alive tunnels.
   goproxy returns false (closing the entire MITM client tunnel) when a
   single upstream round-trip errors. Under load, CDN-fronted registries
   (e.g. Cloudflare for registry.npmjs.org) intermittently reset
   connections, so one transient reset dropped a pooled keep-alive socket
   and surfaced to the package manager as ECONNRESET / "socket hang up".
   Fix: route upstream round-trips through a resilient round tripper that
   retries idempotent, body-less requests with bounded linear backoff,
   absorbing transient resets and keeping the tunnel alive. A reproduction
   test shows the tunnel count drop from 3 to 1 across a transient failure.

2. Head-of-line amplification against the external analysis service.
   Concurrent requests for the same package version each issued their own
   gRPC call. Fix: de-duplicate in-flight analyses with singleflight so a
   burst of identical requests collapses into one upstream call.

3. Per-request goproxy verbose logging on the hot path.
   proxy.Verbose was always on, formatting several log lines per request
   even when discarded below debug level. Fix: enable goproxy verbose
   logging only when PMG runs at debug level.

Note: the hypothesis that the http.Server Read/WriteTimeout leaks onto
hijacked CONNECT tunnels was investigated and disproven (Go clears the
deadlines on Hijack); the behavioral guard tests for long-lived
connections and slow transfers are retained.

* fix: Type assertion error handling

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-02 21:41:57 +05:30
f6e1d9e733 feat: authenticated Malysis analyzer with tenant exclusion support (#313)
* feat: authenticated Malysis analyzer with tenant exclusion support

When SafeDep Cloud credentials are available (keychain or environment),
PMG now uses an authenticated malware analysis query against
api.safedep.io instead of the unauthenticated community endpoint
(community-api.safedep.io). The API key and tenant ID are supplied via
the gRPC connection.

The authenticated response may carry a tenant-specific malicious package
exclusion. This is honored as an opt-in trust signal: a flagged package
is downgraded to allow only when a concrete exclusion (non-empty ID) is
present for the exact package version queried. Exclusions are never
honored for community queries and never weaken the verdict for packages
that were not flagged. Allowed-by-exclusion packages are surfaced as a
warning so the trust decision is never silent.

Changes are additive; non-authenticated usage is unchanged. Credential
resolution is extracted into internal/cloudauth and reused by both the
analyzer factory and the existing cloud sync client.

* fix: surface tenant exclusions in proxy mode; clarify comments

- Warn when proxy interceptor allows a flagged package due to a tenant
  exclusion, matching the guard flow so the trust decision is not silent.
- Remove stray doc comment above warnIfExcluded.
- Clarify that a verified-malware verdict can be downgraded by an
  exclusion in applyExclusion.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-01 15:32:22 +05:30
Sahil BansalandGitHub c3a3518c36 feat: Show cooldown report for pinned version installs (#225)
* feat: Show cooldown report for pinned version installs

When a user installs a package with an explicit version (e.g.
npm install foo@1.2.3) and that version falls within the dependency
cooldown window, the cooldown block is now recorded and shown in the
report. Previously, the report only appeared when ALL versions of a
package were in cooldown (remaining == 0), causing pinned version
installs to fail with a confusing "version not found" error from the
package manager instead of a clear cooldown explanation.

Introduces InterceptorContext to carry per-execution data (pinned
versions) from the CLI command through the interceptor layer, keeping
it separate from long-lived dependencies like the analyzer and cache.

* fix: Normalize PyPI pinned version keys for cooldown lookup

CLI-provided package names (e.g. Flask_Cors) don't match the
URL-parsed form (flask-cors). Normalize keys once at construction
time so cooldown lookups match correctly.

* fix: Handle dots in PyPI package name normalization per PEP 503

denormalizePyPIPackageName already documented [-_.] replacement but
only handled underscores. Now also replaces dots with hyphens so
names like zope.interface match the URL-parsed form zope-interface.

* refactor: Extract shared cooldown stats recording into helper

Deduplicate identical stats-recording blocks from npm_cooldown.go
and pypi_cooldown.go into recordCooldownStats in cooldown.go.

* fix: Distinguish explicit version pins from auto-resolved versions

PyPI parsers resolve all packages to concrete versions (even without
a user-specified constraint), so HasVersion() was always true. Add
IsExplicitVersion to PackageInstallTarget, set it only when the user
provided an explicit constraint. Use it in proxy_flow.go to avoid
false pinned-version cooldown reports.
2026-04-23 17:47:14 +05:30
Abhisek DattaGitHubdevin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>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>
2026-04-11 12:33:27 +05:30
Abhisek DattaandGitHub 0355a5d4fd feat: Add audit system with sink based dispatcher (#211)
* chore: Dependency update

* feat: Add audit system with eventlog as a sink

* fix: Linter fixes

* fix: Code review fixes
2026-04-10 19:07:15 +05:30
Abhisek DattaandGitHub 115531f6b3 perf: Use circuit breaker to fail open Malysis query requests (#196)
* perf: Use circuit breaker to fail open Malysis query requests

* fix: Linter fixes

* test: Add test to confirm grpc status error unwrapping
2026-04-03 21:39:00 +05:30
Abhisek DattaandGitHub 36ac3e3384 feat: Add post-exec reporting support (#134)
* feat: Add post install reporting support

* fix: UI report handling

* fix: Duplicate reporting

* fix: Show warning on insecure bypass

* fix: Proxy event log insecure skip installation

* fix: Proxy event log insecure skip installation

* fix: Common definition for infer outcome
2026-01-27 17:50:26 +05:30
edfdd543e0 chore: README update demo and Error Fix (#126)
* docs: Update README with demo gif

* fix: Proxy remove dependency on interaction

* fix: Update demo gif width

* Update docs/demo/pmg-intro.tape

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com>

* fix: PMG demo

---------

Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-18 16:00:37 +05:30
Abhisek DattaandGitHub 1684e25cc9 fix: Use separate event for trusted package allowed (#98) 2026-01-08 01:03:31 +05:30
Abhisek DattaandGitHub 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
2026-01-08 00:15:02 +05:30
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>
2026-01-07 13:22:08 +05:30