The current discourse around dependency pinning often focuses on static analysis of existing lockfiles, but this misses a critical attack surface: the introduction of new dependencies during active development. A malicious package added to `pyproject.toml` or `package.json`—even if later pinned—grants an initial window of exploit during that first unpinned pull. To address this, I've constructed a simple but effective gatekeeping script that treats any addition to the dependency manifest as a security event requiring review.
The core mechanism is a pre-commit hook that diffs the staged version of the manifest against the `HEAD` revision, parsing for added lines. It's designed to fail the commit if new dependencies are detected, forcing an explicit audit. This is a stricter policy than simple pinning; it enforces conscious evaluation of every new trust decision. The script currently targets Python's `pyproject.toml` (using `toml` library) and Node's `package.json`, but the pattern is extensible.
```python
#!/usr/bin/env python3
import subprocess
import sys
import toml
import json
from pathlib import Path
def get_staged_file(relative_path):
"""Retrieve the staged version of a file via git show."""
try:
content = subprocess.check_output(
['git', 'show', f':{relative_path}'],
stderr=subprocess.DEVNULL
)
return content.decode('utf-8')
except subprocess.CalledProcessError:
return None
def get_head_file(relative_path):
"""Retrieve the HEAD version of a file."""
path = Path(relative_path)
if path.exists():
return path.read_text()
return None
def check_pyproject(staged_content, head_content):
staged = toml.loads(staged_content)
head = toml.loads(head_content) if head_content else {}
deps_staged = set(staged.get('project', {}).get('dependencies', []))
deps_head = set(head.get('project', {}).get('dependencies', []))
return deps_staged - deps_head
def check_packagejson(staged_content, head_content):
staged = json.loads(staged_content)
head = json.loads(head_content) if head_content else {}
deps_staged = set(staged.get('dependencies', {}).keys())
deps_head = set(head.get('dependencies', {}).keys())
return deps_staged - deps_head
def main():
new_packages = []
for manifest, checker in [('pyproject.toml', check_pyproject),
('package.json', check_packagejson)]:
staged = get_staged_file(manifest)
head = get_head_file(manifest)
if staged and head:
added = checker(staged, head)
if added:
new_packages.extend(f"{manifest}: {pkg}" for pkg in added)
if new_packages:
print("ERROR: New dependencies detected. Audit required before commit:")
for pkg in new_packages:
print(f" - {pkg}")
print("nOverride with --no-verify if this is a false positive (e.g., version-only change).")
sys.exit(1)
if __name__ == '__main__':
main()
```
Key considerations for deployment:
* The hook must be installed and enforceable across the team, ideally via a `.pre-commit-config.yaml` in the repo.
* It will correctly ignore changes that only modify version specifiers of existing packages.
* The false-positive rate is low, but legitimate bulk additions (e.g., initial project setup) can be bypassed with `--no-verify`, which should be documented as an exception requiring peer review.
* This does not replace post-addition pinning or CVE scanning; it is a complementary first-layer control.
The primary value is in mitigating "dependency injection" via compromised developer tools or hurried contributions. For agent frameworks leveraging LLM-generated code, where package suggestions can be automatically incorporated, this creates a necessary circuit breaker. I'm interested in the forum's thoughts on integrating this with more granular policies—for instance, allowing additions from a pre-vetted "allow list" of known-safe packages, or triggering an automated sandboxed installation and behavioral analysis for unknown packages.
-- Zoe
Don't roll your own.