From f3e00a7f6e272c54236438b7ce248f7e62315052 Mon Sep 17 00:00:00 2001 From: Abhisek Datta Date: Fri, 5 Jun 2026 18:39:37 +0530 Subject: [PATCH] fix: yarn proxy mode proxy environment injection (#320) --- .github/workflows/pmg-e2e.yml | 41 +++++++++++++++++++++++++++ internal/flows/proxy_flow.go | 3 ++ internal/flows/proxy_flow_env_test.go | 40 ++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 internal/flows/proxy_flow_env_test.go diff --git a/.github/workflows/pmg-e2e.yml b/.github/workflows/pmg-e2e.yml index 11bd5c2..320eaca 100644 --- a/.github/workflows/pmg-e2e.yml +++ b/.github/workflows/pmg-e2e.yml @@ -277,6 +277,47 @@ jobs: test -d node_modules/lodash cd .. && rm -rf bun-test + # Runs before the classic-yarn step so corepack's Berry shim stays the `yarn` + # on PATH (the later `npm install -g yarn@1.22.22` would shadow it). + - name: Test safedep-test-pkg is Blocked using Yarn (Berry) Proxy mode + env: + COREPACK_ENABLE_DOWNLOAD_PROMPT: "0" + YARN_NODE_LINKER: node-modules + YARN_ENABLE_HARDENED_MODE: "0" + # CI=true would enable immutable installs and fail before the proxy can block. + YARN_ENABLE_IMMUTABLE_INSTALLS: "false" + run: | + echo "Testing that safedep-test-pkg is blocked with Yarn Berry in proxy mode..." + YARN_BERRY_TESTDIR=$(mktemp -d) && cd "$YARN_BERRY_TESTDIR" + npm init -y + + # Pin Yarn Berry without running an install (corepack use would trigger one). + npm pkg set packageManager=yarn@4.16.0 + yarn --version + + # A non-zero exit alone is not enough — yarn could fail for unrelated + # reasons (setup, network, CA trust) and falsely pass. Require PMG's + # malware block signal in the output. + if output=$(pmg yarn add safedep-test-pkg@0.1.3 2>&1); then + echo "$output" + echo "ERROR: safedep-test-pkg was not blocked with Yarn Berry!" + exit 1 + fi + echo "$output" + if ! echo "$output" | grep -q "Malicious package blocked"; then + echo "ERROR: yarn add failed, but not due to a PMG malware block" + exit 1 + fi + echo "SUCCESS: safedep-test-pkg correctly blocked with Yarn Berry" + + if [ -d "node_modules/safedep-test-pkg" ]; then + echo "ERROR: safedep-test-pkg found in node_modules!" + exit 1 + else + echo "SUCCESS: safedep-test-pkg not present in node_modules" + fi + cd - && rm -rf "$YARN_BERRY_TESTDIR" + - name: Test Yarn - Single Package & Manifest run: | echo "Testing Yarn single package installation..." diff --git a/internal/flows/proxy_flow.go b/internal/flows/proxy_flow.go index 91a4932..f8b2c1b 100644 --- a/internal/flows/proxy_flow.go +++ b/internal/flows/proxy_flow.go @@ -423,6 +423,9 @@ func (f *proxyFlow) setupEnvForProxy(proxyAddr, caCertPath string) []string { fmt.Sprintf("HTTPS_PROXY=%s", proxyURL), fmt.Sprintf("NO_PROXY=%s", noProxyList), fmt.Sprintf("NODE_EXTRA_CA_CERTS=%s", caCertPath), + fmt.Sprintf("YARN_HTTP_PROXY=%s", proxyURL), + fmt.Sprintf("YARN_HTTPS_PROXY=%s", proxyURL), + fmt.Sprintf("YARN_HTTPS_CA_FILE_PATH=%s", caCertPath), fmt.Sprintf("http_proxy=%s", proxyURL), fmt.Sprintf("https_proxy=%s", proxyURL), fmt.Sprintf("no_proxy=%s", noProxyList), diff --git a/internal/flows/proxy_flow_env_test.go b/internal/flows/proxy_flow_env_test.go new file mode 100644 index 0000000..2fb9dca --- /dev/null +++ b/internal/flows/proxy_flow_env_test.go @@ -0,0 +1,40 @@ +package flows + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func envToMap(env []string) map[string]string { + m := make(map[string]string, len(env)) + for _, e := range env { + k, v, ok := strings.Cut(e, "=") + if ok { + m[k] = v + } + } + return m +} + +// TestSetupEnvForProxyConfiguresYarn proves the root cause of #319: yarn Berry +// (yarn 2+) ignores the standard HTTP_PROXY/HTTPS_PROXY env vars and only honors +// its own config, which can be set via YARN_* env overrides. Without these, +// yarn bypasses the MITM proxy entirely and no packages are analyzed. +func TestSetupEnvForProxyConfiguresYarn(t *testing.T) { + f := &proxyFlow{} + const proxyAddr = "127.0.0.1:54321" + const caCertPath = "/tmp/pmg-ca-cert.pem" + + env := envToMap(f.setupEnvForProxy(proxyAddr, caCertPath)) + + proxyURL := "http://" + proxyAddr + + assert.Equal(t, proxyURL, env["YARN_HTTP_PROXY"], + "yarn ignores HTTP_PROXY; YARN_HTTP_PROXY is required to route yarn through the proxy") + assert.Equal(t, proxyURL, env["YARN_HTTPS_PROXY"], + "yarn ignores HTTPS_PROXY; YARN_HTTPS_PROXY is required to route yarn through the proxy") + assert.Equal(t, caCertPath, env["YARN_HTTPS_CA_FILE_PATH"], + "yarn ignores NODE_EXTRA_CA_CERTS; YARN_HTTPS_CA_FILE_PATH is required to trust the MITM CA") +}