BehaviorLock shows how the observed install behavior of an npm package changes between two exact versions.
Source review and vulnerability databases answer important questions about a dependency update. BehaviorLock asks another one: did the new version begin reading a credential path, starting a shell, changing files, or attempting a network connection when its install scripts ran?
It records selected Linux system calls from both versions, normalizes the results, and produces a diff that a person can inspect or a CI job can evaluate.
Warning
BehaviorLock is an experimental observation tool. It is not a malware sandbox and does not prove that a package is safe. Unknown packages belong on an ephemeral GitHub hosted runner or a disposable virtual machine, never on a personal workstation.
Imagine that version 1.0.0 creates a cache directory during installation. Version 1.1.0 does the same thing, but also starts a shell and tries to read an SSH key path.
BehaviorLock reports the shell launch and credential path read as new observations. It does not decide why they happened. A maintainer reviews the evidence and decides whether the change is expected.
| Question | Current answer |
|---|---|
| Maturity | Public experiment, 0.1.0-dev |
| Package ecosystem | Public npm registry packages |
| Version input | Exact semantic versions only |
| Observed environment | Linux container install lifecycle |
| CLI build and unit tests | Linux and macOS |
| Full Docker integration | GitHub hosted Linux runner |
| Native Windows or macOS tracing | Not supported |
| Profile authenticity | Unsigned, not attested |
| Tagged release | None |
The current parser records a bounded subset of:
- File reads, writes, creation, deletion, renaming, and permission changes
- Executable launches and up to 32 visible arguments
- Network connection attempts
- Whether an observed call succeeded, failed, or was blocked
The capture path exercises npm preinstall, install, and postinstall scripts through npm rebuild. It does not observe normal application runtime behavior.
exact npm version
|
v
prepare without lifecycle scripts
|
v
resolve immutable package filesystem
|
v
run lifecycle offline under strace
|
v
validate and normalize a profile
|
v
compare two compatible profiles
|
v
JSON, text, or Markdown report
Preparation and execution are separate. Preparation needs registry access and runs with lifecycle scripts disabled. Execution starts from the prepared filesystem, has no network, uses a read only root filesystem, receives no host mounts or inherited credentials, and runs package code as uid 65532 with zero effective capabilities.
The preparation network is still a risk. Package metadata and transitive dependency metadata can influence what npm fetches. Use capture only on a disposable runner that cannot reach sensitive private networks or cloud metadata.
Go 1.23 or newer is required.
go build -trimpath -o bin/behaviorlock ./cmd/behaviorlock
bin/behaviorlock profile \
--package example@1.0.0 \
--trace testdata/traces/baseline.strace \
--output baseline.profile.json
bin/behaviorlock profile \
--package example@1.1.0 \
--trace testdata/traces/candidate.strace \
--output candidate.profile.json
bin/behaviorlock compare \
--allow-external \
--baseline baseline.profile.json \
--candidate candidate.profile.json \
--format markdown \
--output behaviorlock.report.mdThese traces are inert fixtures. Profiles created with profile --trace are marked external-unverified. Comparison rejects them unless --allow-external is present because their capture conditions and provenance cannot be verified.
Docker is required. Build the pinned runner image from this repository first.
make runner
make build
bin/behaviorlock doctor
bin/behaviorlock capture \
--experimental \
--package is-number@7.0.0 \
--timeout 2m \
--output is-number.profile.json--experimental is mandatory. The command records the exact runner image ID, architecture, Node version, npm version, strace version, package registry integrity, and dependency lock digest. Docker execution uses immutable image IDs after resolution so a mutable local tag cannot silently change the captured environment.
Do not capture an unknown package on a machine that contains valuable data, credentials, trusted workloads, or access to private infrastructure.
Both profiles must describe the same package and use the same runner image ID, architecture, Node version, npm version, strace version, network mode, sandbox profile, and coverage scope.
bin/behaviorlock compare \
--baseline package-1.0.0.profile.json \
--candidate package-1.1.0.profile.json \
--fail-on high \
--format markdown \
--output behaviorlock.report.mdThe default threshold is high. Exit code 1 means an added observation reached the selected threshold. It does not mean the package is malicious.
| Rule | Level | Meaning |
|---|---|---|
BL100 |
Critical | New access to a common credential or secret path |
BL200 |
High | New network connection attempt during offline execution |
BL300 |
High | New shell, downloader, or remote access process |
BL301 |
Medium | New executable process |
BL400 |
High | New mutation outside disposable work and temporary roots |
BL401 |
Medium | New mutation inside a disposable writable root |
BL402 |
Medium | New deletion or permission change |
BL500 |
Low | New file read or metadata inspection |
A pass verdict means no added observation reached the comparison rule. It does not authenticate the input profiles, establish full coverage, or prove safety.
behaviorlock doctor
behaviorlock capture --experimental --package name@1.2.3 --output profile.json
behaviorlock profile --package name@1.2.3 --trace raw.strace --output profile.json
behaviorlock compare --baseline old.json --candidate new.json --output report.json
behaviorlock validate --profile profile.json
behaviorlock version
Exit codes:
0means the command completed and the comparison threshold was not reached.1means a comparison reached the selected review threshold.2means invalid input, incomplete evidence, sandbox failure, or another runtime error.
The Go parser and comparison code build and run on Linux and macOS. The capture backend observes Linux behavior because it depends on Linux containers, Linux permissions, and strace.
Docker Desktop may allow a macOS or Windows host to operate a Linux container, but that still produces a Linux profile. Native Windows and native macOS behavior are outside this version, and Windows is not yet part of the CI build matrix.
See platform support for the exact distinction between host compatibility and observed target behavior.
The capture backend uses defense in depth:
- Strict package input validation before Docker runs
- Docker argument arrays instead of host shell interpolation
- No host mounts, Docker socket, inherited home directory, inherited credentials, or Docker client proxy variables
- Offline lifecycle execution and a read only root filesystem
- Bounded memory, CPU, processes, descriptors, temporary storage, output, and wall clock time
- A root owned trace directory that package code cannot read or modify
- Immutable Docker content IDs for the runner and prepared package filesystem
- Required trace sentinels, completion evidence, and an empty tracer diagnostic channel
Containers still share a kernel. Package code can detect tracing, stay dormant, exploit a runtime vulnerability, or behave differently outside the harness. Profiles are unsigned JSON. validate checks structure and internal consistency, not authenticity.
Read the threat model and the limitations before using capture as part of a security decision.
- User guide explains the tool without requiring security expertise.
- Technical reference documents the pipeline, data model, comparability rules, and failure behavior.
- Platform support describes Linux, macOS, and Windows support.
- Security audit records the latest review, fixes, scan evidence, and remaining risks.
- Architecture describes component boundaries.
- Threat model lists assets, hostile inputs, controls, and residual risk.
- Limitations states what BehaviorLock cannot observe or prove.
- Roadmap contains the release gates.
- Security policy explains private vulnerability reporting.
make check
make buildDocker integration runs separately:
make integrationThe protected ci-required job runs race enabled tests, shell checks, schema checks, vulnerability scanning, DCO verification, and the hardened Docker integration. CodeQL and scheduled parser fuzzing run in separate workflows.
BehaviorLock is a public experiment with no tagged release. The parser and comparison core are usable now. The capture backend remains experimental until every adversarial gate in ROADMAP.md passes, trusted profiles have verifiable provenance, and the acquisition network boundary is stronger.
Profiles and reports can retain sensitive paths and package controlled strings. Review every artifact before attaching it to an issue, publishing it, or committing it.
Start with CONTRIBUTING.md. Human commits require Developer Certificate of Origin 1.1 signoff. Security vulnerabilities belong in GitHub private vulnerability reporting, not public issues.
This repository uses the owner qualified name kiranmagic7/behaviorlock. It is independent of the earlier christian140903-sudo/behaviorlock project, which checks AI agent compatibility. The projects have different purposes and no affiliation.
BehaviorLock does not claim ownership of the phrase "Bill of Behavior" and does not present itself as a standard. ORIGINS.md records adjacent projects and the boundaries of this implementation.
Apache License 2.0. Contributions are accepted under the same license through Developer Certificate of Origin 1.1 signoff.