Packj
Packj stops :zap: Solarwinds-, ESLint-, and PyTorch-like attacks by flagging malicious/vulnerable open-source dependencies ("weak links") in your software supply-chain
Install / Use
/learn @ossillate-inc/PackjREADME
<img src="https://packj.dev/static/img/icons/package.svg" width="45"/> <span style="font-size: 42px"> Packj flags malicious/risky open-source packages</span>
Packj (pronounced package) is a tool to help to mitigate software supply chain attacks. It can detect malicious, vulnerable, abandoned, typo-squatting, and other "risky" packages from popular open-source package registries, such as NPM, RubyGems, and PyPI. It can be easily customized to minimize noise. Packj started as a PhD research project and is currently being developed under various govt grants.
Note Self-hosted Packj webserver and several integrations coming later this month :punch: Watch this repo to stay up to date.
Contents
- Get started - available as Docker image, GitHub Action, and packages
- Functionality - deep static/dynamic code analysis and sandboxing
- Supported ecosystems - NPM, PyPI, Rubygems, PHP, Rust
- Our story - started as a PhD research project and is backed by govt grants
- Why Packj - existing CVE scanners ASSUME code is BENIGN and not analyze its behavior
- Customization - turn off alerts as per your threat model to reduce noise
- Malware found - reported over 70 malicious PyPI and RubyGems packages
- Talks and videos - presentations from PyCon, OpenSourceSummit, BlackHAT
- Project roadmap - view or suggest new features; join our discord channel
- Team and collaboration - lead by Cybersecurity researchers from academia/industry
- FAQ - supported package managers, commonly asked questions on techniques, and more
Get started
We support multiple deployment models:
1. GitHub runner
Use Packj to audit dependencies in pull requests.
- name: Packj Security Audit
uses: ossillate-inc/packj-github-action@v0.0.10-beta
with:
# TODO: replace with your dependency files in the repo
DEPENDENCY_FILES: pypi:requirements.txt,npm:package.json,rubygems:Gemfile
REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
View on GitHub marketplace. Example PR run.
2. Docker image (recommended)
The quickest way to try/test Packj is using Docker. Podman is also supported for containerized (isolated) runs.
docker run -v /tmp:/tmp/packj -it ossillate/packj:latest --help
3. Source repo
Clone this repo,
git clone https://github.com/ossillate-inc/packj.git && cd packj
Install dependencies
bundle install && pip3 install -r requirements.txt
Start with help:
python3 main.py --help
Supported ecosystems
Packj can vet pubished packages from NPM, PyPI, Rust, PHP, and Rubygems package registries. Rust and PHP support is WIP. We're actively adding support for registries. It also supports vetting local (unpublished) NPM and PyPI packages.
| Registry | Ecosystem | Supported | | --------- | ---------- | ------------------ | | NPM | JavaScript | :white_check_mark: | | PyPI | Python | :white_check_mark: | | Cargo | Rust | :white_check_mark: | | Rubygems | Ruby | :white_check_mark: | | Packagist | PHP | :white_check_mark: | | Docker | Docker | :x: | | Nuget | .NET | :white_check_mark: | | Maven | Java | :white_check_mark: | | Cocoapods | Swift | :x: |
Functionality
Packj offers the following tools:
Auditing a package
Packj audits open-source software packages for "risky" attributes that make them vulnerable to supply chain attacks. For instance, packages with expired email domains (lacking 2FA), large release time gap, sensitive APIs or access permissions, etc. are flagged as risky.
Auditing the following is supported:
- multiple packages:
python3 main.py audit -p pypi:requests rubygems:overcommit - dependency files:
python3 main.py audit -f npm:package.json pypi:requirements.txt
By default, audit only performs static code analysis to detect risky code. You can paas -t or --trace flag to perform dynamic code analysis as well, which will install all requested packages under strace and monitor install-time behavior of packages. Please see the example output below.
$ docker run -v /tmp:/tmp/packj -it ossillate/packj:latest audit --trace -p npm:browserify
[+] Fetching 'browserify' from npm..........PASS [ver 17.0.0]
[+] Checking package description.........PASS [browser-side require() the node way]
[+] Checking release history.............PASS [484 version(s)]
[+] Checking version........................RISK [702 days old]
[+] Checking release time gap............PASS [68 days since last release]
[+] Checking author.........................PASS [mail@substack.net]
[+] Checking email/domain validity.......RISK [expired author email domain]
[+] Checking readme.........................PASS [26838 bytes]
[+] Checking homepage.......................PASS [https://github.com/browserify/browserify#readme]
[+] Checking downloads......................PASS [2M weekly]
[+] Checking repo URL.......................PASS [https://github.com/browserify/browserify]
[+] Checking repo data...................PASS [stars: 14189, forks: 1244]
[+] Checking if repo is a forked copy....PASS [original, not forked]
[+] Checking repo description............PASS [browser-side require() the node.js way]
[+] Checking repo activity...............PASS [commits: 2290, contributors: 207, tags: 413]
[+] Checking for CVEs.......................PASS [none found]
[+] Checking dependencies...................RISK [48 found]
[+] Downloading package from npm............PASS [163.83 KB]
[+] Analyzing code..........................RISK [needs 3 perm(s): decode,codegen,file]
[+] Checking files/funcs....................PASS [429 files (383 .js), 744 funcs, LoC: 9.7K]
[+] Installing package and tracing code.....PASS [found 5 process,1130 files,22 network syscalls]
=============================================
[+] 5 risk(s) found, package is undesirable!
=> Complete report: /tmp/packj_54rbjhgm/report_npm-browserify-17.0.0_hlr1rhcz.json
{
"undesirable": [
"old package: 702 days old",
"invalid or no author email: expired author email domain",
"generates new code at runtime",
"reads files and dirs",
"forks or exits OS processes",
]
}
</details>
WARNING: since packages could execute malicious code during installation, it is recommended to ONLY use
-tor--tracewhen running inside a Docker container or a Virtual Machine.
Audit can also be performed in Docker/Podman containers. Please find details on risky attributes and how to use at Audit README.
Sandboxed package installation
Packj offers a lightweight sandboxing for safe installation of a package. Specifically, it prevents malicious packages from exfiltrating sensitive data, accessing sensitive files (e.g., SSH keys), and persisting malware.
It sandboxes install-time scripts, including any native compliation. It uses strace (i.e., NO VM/Container required).
Please find details on the sandboxing mechanism and how to use at Sandbox README.
<details> <summary><h4>Show example run/output</h4></summary>$ python3 main.py sandbox gem install overcommit
Fetching: overcommit-0.59.1.gem (100%)
Install hooks by running `overcommit --install` in your Git repository
Successfully installed overcommit-0.59.1
Parsing documentation for overcommit-0.59.1
Installing ri documentation for overcommit-0.59.1
#############################
# Review summarized activity
#############################
[+] Network connections
[+] DNS (1 IPv4 addresses) at port 53 [rule: ALLOW]
[+] rubygems.org (4 IPv6 addresses) at port 443 [rule: IPv6 rules not supported]
[+] rubygems.org (4 IPv4 addresses) at port 443 [rule: ALLOW]
[+] Filesystem changes
/
└── home
└── ubuntu
└── .ruby
├── gems
│ ├── iniparse-1.5.0 [new: DIR, 15 files, 46.6K bytes]
│ ├── rexml-3.2.5 [new: DIR, 77 files, 455.6K bytes]
│ ├── overcommit-0.59.1 [new: DIR, 252 files, 432.7K bytes]
│ └── childprocess-4.1.0 [new: DIR, 57 files, 141.2K bytes]
├── cache
│ ├──
