Run [ npm i ] safely, audit installs inside a docker container.
macOS · Linux · Windows | Go stdlib only | MIT
# instead of: npm i
ward .Run npm install inside an isolated Docker container on macOS, Linux, or Windows. ward captures outbound traffic (including lifecycle scripts), prints a safe / not safe summary, and only copies node_modules to your machine after you approve.
- macOS, Linux, or Windows
- Docker running (Docker Desktop on macOS/Windows; Docker Engine on Linux)
- Go 1.22+ (to build from source)
Clone the repo and build ward on your machine:
git clone https://github.com/AdamGonda/ward.git
cd ward
go build -o ward ./cmd/wardOr install into your Go bin directory (must be on PATH):
go install ./cmd/wardThen run ward --help.
ward <project-directory> [--json] [--verbose] [--auto-approve]cd my-app
ward .- Builds the Docker image (
node:22-alpine+tcpdump) from embedded assets if needed - Runs
npm installin a container with your project mounted at/workspace - Captures DNS/HTTP/HTTPS traffic during install (including lifecycle scripts)
- Prints a security summary (safe or not safe) with unexpected destinations
- Prompts: Approve install
[y/N] - On approval, copies
node_modulesfrom the container to your project
Artifacts are written under <project>/.ward/:
tcpdump.log— raw capture lognpm-install.log— npm output (lifecycle script detection)install-phases.json— capture/install timing markersnetwork-summary.json— install metadata from the containernetwork-report.txt— full semantic security report
Add to your app .gitignore:
.ward/
| Flag | Description |
|---|---|
--json |
Print security report as JSON (destinations, phases, approval state) |
--verbose |
Include internal/Docker network traffic in the report |
--strict |
Exit with error if the security report is not safe (use with --auto-approve in CI) |
--auto-approve |
Approve and copy node_modules without prompting (use with --strict in CI) |
-h, --help |
Show help |
Use --auto-approve and --strict together: no prompt, fail if the security report is not safe, then copy node_modules. On Linux runners the container matches the host (native addons like sharp work after copy). Add --json to read approved and copied in scripts.
ward . --auto-approve --strictExample (Docker required on the runner):
- run: go build -o ward ./cmd/ward
- run: ./ward . --auto-approve --strictThe tool produces human-readable output instead of raw packet dumps:
Full report: .ward/network-report.txt
SAFE
✓ Only known hosts contacted
✓ No postinstall requestsWhen something looks wrong (e.g. unknown domain during postinstall):
Full report: .ward/network-report.txt
NOT SAFE
⚠ Unexpected domain contacted during lifecycle script
Unexpected destinations (1):
⚠ example.com — Unexpected outbound traffic (not on allowlist)
Phase: lifecycle:postinstallThe full report in network-report.txt groups traffic by install phase (package fetch vs lifecycle scripts), labels known hosts (npm registry, GitHub, etc.), and includes a detailed risk breakdown for auditing.
| Code | Meaning |
|---|---|
| 0 | Completed — install reviewed; node_modules copied if you approved, otherwise left unchanged |
| 1 | Security report is not safe (--strict) |
| 2 | Usage/prerequisite error |
| 3 | npm install failed in container |
Declining the copy prompt is not an error (exit 0). Use --json and check approved / copied in scripts.
The container runs Linux (Alpine). On macOS or Windows, packages with native binaries (e.g. sharp, esbuild, @swc/core) are built for Linux inside the container. Copying node_modules to your machine may cause runtime errors on the host.
On Linux (including CI), the container and host match — copied native modules work as expected.
This tool is especially useful for:
- Auditing network behavior during install (any platform)
- CI pipelines on Linux (
--auto-approve --strict) - Mostly pure-JavaScript dependency trees on macOS/Windows
- Teams that accept re-running
npm rebuildor a host-side install after review on macOS/Windows
sequenceDiagram
participant CLI as GoCLI
participant Docker as Docker
participant Container as AlpineContainer
CLI->>Docker: embed_extract_and_build
CLI->>Docker: docker_run_mount_project
Container->>Container: tcpdump_npm_install
CLI->>CLI: analysis_BuildSecurityReport
CLI->>CLI: prompt_approve_deny
alt approved
CLI->>Docker: docker_cp_node_modules
end
CLI->>Docker: docker_rm
Smoke test project (lodash only):
ward examples/smokePostinstall network fixture (wget to example.com during postinstall):
ward examples/postinstall-networkgo test ./...Docker image assets live in docker/ and are embedded at compile time via docker/assets.go.
Future enhancements (not yet implemented):
- Process monitoring — detect
curl,wget,bashspawned during lifecycle scripts --sandboxmode — restricted filesystem and network egress- Multi-ecosystem — pnpm, bun, pip, cargo with the same analysis engine
MIT