Skip to content

feat(proxy): persistent proxy server (POC)#347

Closed
Sahilb315 wants to merge 11 commits into
mainfrom
feat/persistent-proxy
Closed

feat(proxy): persistent proxy server (POC)#347
Sahilb315 wants to merge 11 commits into
mainfrom
feat/persistent-proxy

Conversation

@Sahilb315

Copy link
Copy Markdown
Contributor

Summary

  • Adds pmg proxy command group with start, stop, env, and status subcommands
  • The proxy runs persistently in the foreground, intercepting all npm/pip traffic without requiring PMG shim wrappers or aliases
  • Designed for CI/CD pipelines (GitHub Actions) where env vars can be set globally for all steps

How it works

# Start once (background in CI)
pmg proxy start &

# Inject env vars into the environment
eval $(pmg proxy env)       # local shell
pmg proxy env --gha         # GitHub Actions ($GITHUB_ENV)

# All subsequent package manager calls are intercepted automatically
npm install                 # no `pmg npm` wrapper needed
pip install requests

GHA example

- run: pmg proxy start &
- run: |
    sleep 1
    pmg proxy env --gha
- run: npm install          # intercepted, no pmg wrapper
- run: pip install -r requirements.txt

Changes

  • internal/proxystate — state file (pid/addr/ca-cert-path) with liveness check
  • internal/flows/cert.go — extracted shared SetupCACertificate (removes duplication from proxy_flow.go)
  • cmd/proxy/{start,stop,env,status}.go — command implementations
  • .github/workflows/persistent-proxy-e2e.yml — E2E test for persistent proxy mode (trigger via workflow_dispatch on this branch)

…ommands

Adds pmg proxy command group for running a long-lived MITM proxy that
intercepts all package manager traffic without requiring PMG shims or
wrappers. Targets CI/CD pipelines where env vars can be set globally.

- pmg proxy start: starts proxy with npm+pypi interceptors, writes
  state file (pid/addr/ca-cert-path), auto-blocks suspicious packages
- pmg proxy stop: sends SIGTERM to the running proxy
- pmg proxy env: emits HTTP_PROXY/HTTPS_PROXY/SSL_CERT_FILE etc. as
  shell exports, or writes directly to $GITHUB_ENV with --gha
- pmg proxy status: shows running/stopped status

GHA usage:
  pmg proxy start &
  pmg proxy env --gha   # populates env for all subsequent steps
  npm install           # intercepted automatically, no wrapper needed

Also adds .github/workflows/persistent-proxy-e2e.yml to validate the
persistent proxy mode end-to-end in CI.
@safedep

safedep Bot commented Jun 23, 2026

Copy link
Copy Markdown

SafeDep Report Summary

Green Malicious Packages Badge Green Vulnerable Packages Badge Green Risky License Badge

No dependency changes detected. Nothing to scan.

View complete scan results →

This report is generated by SafeDep Github App

@codecov-commenter

codecov-commenter commented Jun 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 13.61868% with 222 lines in your changes missing coverage. Please review.
✅ Project coverage is 54.57%. Comparing base (d360e75) to head (ea1d697).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
cmd/proxy/start.go 0.00% 69 Missing ⚠️
cmd/proxy/env.go 0.00% 60 Missing ⚠️
cmd/proxy/stop.go 0.00% 38 Missing ⚠️
cmd/proxy/status.go 0.00% 19 Missing ⚠️
internal/flows/cert.go 47.05% 13 Missing and 5 partials ⚠️
cmd/proxy/proxy.go 0.00% 9 Missing ⚠️
internal/proxystate/state.go 66.66% 4 Missing and 4 partials ⚠️
main.go 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #347      +/-   ##
==========================================
+ Coverage   54.28%   54.57%   +0.28%     
==========================================
  Files         184      197      +13     
  Lines       13131    13771     +640     
==========================================
+ Hits         7128     7515     +387     
- Misses       5324     5559     +235     
- Partials      679      697      +18     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

pmg proxy stop now waits for the proxy process to exit, reads the final
blocked count written to the state file on shutdown, and exits non-zero
when one or more packages were blocked during the session.

This gives CI a clear failure signal from the proxy itself, separate
from the package manager's own exit code.
…cked

Remove the if-wrapper that was swallowing the exit code. The step now
fails naturally when stop exits 1, which is the correct CI behavior —
a blocked package should fail the job.
run: echo "$GITHUB_WORKSPACE/bin" >> $GITHUB_PATH

- name: Setup PMG
run: pmg setup install

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we do --system here so that the certs are installed at the system level and we don't have to rely on environment for cert config? Would required sudo pmg setup install --system I think.

pmg proxy env can check if servers are installed as system level, if not, add the cert related env else skip it? Something like how PMG proxy flow works today.

Both cases work.

The reason I am kind leaning towards system installation is, in future we can "enforce" proxy via. iptables so that something like sudo npx x also works. Btw. sudo scrubs env so env based proxy config will not work if something is executed via. sudo


- name: Start persistent proxy
run: |
pmg proxy start &

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would really prefer something like:

pmg proxy start --daemon | -D --state /custom/state/override

By default, pmg proxy start starts the proxy server as a foreground process. But --daemon can be used to daemonize the process, which has specific semantic on Linux/MacOS and Windows so, daemonize likely is a platform specific concern.

The default state storage can be cacheDir relative but user should be able to override it. Useful in CI setup where customization is useful.

run: |
pmg proxy start &
# Poll until proxy writes its state file (up to 10s)
for i in $(seq 1 10); do

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not required if we daemonize the proxy server.

done

- name: Inject proxy env vars into workflow environment
run: pmg proxy env --gha

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we are overloading concerns here. Its unnecessary coupling with CI env. It can be as simple as pmg proxy env which:

  1. Checks state file for port and anything else
  2. Prepares the env variables
  3. Prints the env variables

For GHA, I can just do: echo $(pmg proxy env) ?? $GITHUB_ENV or something like that. Works in a generic way across any environment.

For shell, I would just do:

export $(pmg proxy env | xargs)

python -m venv venv && source venv/bin/activate
pip install requests==2.32.4
python -c "import requests; print('pip ok:', requests.__version__)"
deactivate && rm -rf venv

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add test for the case of malicious package installation via. python3 -m pip install x


- name: Start persistent proxy
run: |
pmg proxy start &

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will the proxy server work if I run it as root, something like sudo proxy start --daemon --state /root/pmg-proxy.state


- name: Stop proxy
if: always()
run: pmg proxy stop

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By default pmg proxy stop should just stop the proxy and not fail due to policy violations. I feel that is non-intuitive. Failing on policy violations to block CI should be something like:

pmg proxy stop --fail-on-violation that clearly states that the stop command fails if any violation (policy or malicious package) detected.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A good UX will be for pmg proxy stop --violations to print all the violations and pmg proxy stop --violations --json /tmp/a.json to write the violations in a JSON file so that the GitHub Action can read the JSON and add it as a formatted comment in GitHub PR.

But it can be a separate enhancements (UX).

@Sahilb315

Copy link
Copy Markdown
Contributor Author

Superseded by #351 — the persistent proxy server MVP. #351 is a clean branch off main with the full feature (daemonization, configurable state/port, generic env, opt-in fail-on-violation, cloud event sync, and the GitHub Action server-mode), and incorporates the review feedback from this PR. Closing the POC in favor of it.

@Sahilb315 Sahilb315 closed this Jun 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants