DevShield · local install firewall

Harden your npm ci and pip install.

DevShield is a local guard for dependency installs. You wrap the install command; it kills hostile lifecycle scripts, requires a pinned lockfile, flags exposed secrets, and blocks off-allowlist network connections — all on your machine, zero external dependencies.

i

DevShield is a checkpoint tool: it only protects installs you route through it. A bare npm ci run alongside is invisible to it. You harden your CI and your blessed local path — not "install this and you're safe everywhere".

Install

Build it, put it on PATH.

No external dependencies: a stable Rust toolchain is all you need.

# from the Proxia repo cd devshield cargo build --release # binary → target/release/devshield cp target/release/devshield /usr/local/bin/ # optional: onto PATH
The core command

devshield run — wrap an install.

DevShield runs your command, applies the gates you enabled, and (with --audit-log) writes a local JSONL trail. Every gate defaults to warn (non-destructive); you escalate to block or fail when you want to harden.

devshield run (--profile | --policy ) [--scripts-mode ] [--preflight-secrets] [--preflight-mode ] [--preflight-lockfile] [--lockfile-mode ] [--egress-proxy] [--egress-mode ] [--audit-log ] -- <command> [args...]
The four gates

Four layers, toggled one by one.

Each is independent and non-destructive by default. Stacked, they cover the most common install vectors.

// 1 · execution

Lifecycle scripts

--scripts-mode warn|block|fail

block disables npm install scripts (injects --ignore-scripts); fail refuses (exit 21) before spawning. Also gates npx / npm exec / pnpm dlx as arbitrary code execution. pip: warn-only. The layer that stops Shai-Hulud / Axios-style payloads.

// 2 · integrity

Lockfile posture

--preflight-lockfile [--lockfile-mode warn|fail]

Flags (or refuses, exit 22) a missing/unhashed npm lockfile, or a command that mutates the lockfile (npm install) instead of respecting it (npm ci). Posture, not tamper detection — npm already verifies hashes. pip: best-effort, warn-only.

// 3 · secrets

Secret preflight

--preflight-secrets [--preflight-mode warn|fail]

Reports secret-bearing env vars and files (.env, .npmrc, .pypirc…) before the install runs — without ever printing the values. fail → exit 20.

// 4 · network

Egress proxy

--egress-proxy [--egress-mode warn|enforce]

A local (127.0.0.1) proxy the install is routed through. enforce blocks (403) any connection to an off-allowlist host. No MITM: blocks by host, never by content. warn logs everything; a bind failure in enforce → exit 23 (fail-closed).

Modes · scripts-mode
ModeEffectDEVSHIELD_MODE
warndefault

Runs unchanged; logs a lifecycle_scripts event.

audit-only
block

Disables scripts for npm (injects --ignore-scripts + npm_config_ignore_scripts). pip / npx: warn-only.

enforce
fail

Exits 21 before spawning if scripts would run. Unrecognized command → refused (fail-closed).

enforce
!

Coverage caveat:block script-disabling is verified for npm. yarn Berry (v2+) ignores npm_config_* and doesn't honor --ignore-scripts; verify before relying on it for pnpm/yarn.

Exit codes

One code per refusal — scriptable in CI.

CodeMeaning
0

Child ran; no gate refused.

child code

Passed through from the wrapped command.

20

--preflight-mode fail: secret risk detected.

21

--scripts-mode fail: lifecycle scripts would run.

22

--lockfile-mode fail: lockfile posture risk.

23

--egress-mode enforce: proxy could not start (fail-closed).

10

check / observe egress: destination blocked by policy.

2

Usage / argument error.

125 · 127

Audit-log write failure (125) · command execution failure (127).

Adoption · CI first

The highest-value anchor.

It's where supply-chain attacks actually land, it's central, and it's impossible to forget. Swap one line and fail the build closed on risk:

# .github/workflows/ci.yml — hardened build - run: | devshield run --profile npm \ --scripts-mode fail \ --preflight-lockfile --lockfile-mode fail \ --preflight-secrets --preflight-mode fail \ --egress-proxy --egress-mode enforce \ --audit-log devshield-audit.jsonl \ -- npm ci - run: devshield audit read --audit-log devshield-audit.jsonl # trail in the build log

Other anchors: a shell alias (alias npm='devshield run … -- npm') for transparent local use; a shared Makefile / just target for the team; a dev container's postCreateCommand.

Audit trail

A local, readable JSONL trail.

devshield audit read --audit-log <file> # summary of every event devshield audit trace --audit-log <file> --context-id <id> # one command + its egress

Events: command_run, lifecycle_scripts, secret_preflight, lockfile_preflight, egress_observation. All local; nothing is transmitted.

Honesty · what DevShield is not

Containment, not a magic wand.

// scope

Routed installs only

No system-wide interception. A bare npm ci is invisible. The shell alias comes closest, but is opt-in and bypassable.

// nature

Containment, not integrity

--scripts-mode stops execution/exfiltration; it won't tell you a package was swapped. npm already verifies integrity on install.

// network

Host, not content

The proxy blocks by host:port, no MITM. It can't tell a download from a publish on an allowlisted host. github.com stays allowed by default.

// destruction

No filesystem defense

A postinstall that wipes files is stopped only if scripts are disabled (block/fail). No network control helps here.

Use DevShield to harden your CI and your blessed local path, layered with lockfile pinning and package provenance — not as "install this and you're safe everywhere".