StayAwakeBot
Supply-chain worm hunting for developer machines and CI.
A single compromised dependency is how one package becomes an organisation-wide incident. Self-propagating packages spread through installs, builds and merges — arriving with code you asked for, and running before you ever read it.
saw hunts them where they land: in your repositories, your lockfiles, your installed
dependency tree, and your machine's start-up surface. It then remediates through a pull request
and gates CI, so an infected change cannot merge.
Offline and accurate with zero flags. A default scan needs no network and no configuration.
saw scan's exit code is the verdict, so a CI gate is one line.
| Detect | saw scan — repositories, lockfiles and installed dependency trees. Read-only, always. |
| Remediate | saw fix — prepares a cleanup branch per infected repo, --pr to publish. Never rewrites history; saw discard undoes it. |
| Prevent | saw guard — install and verify the CI gate on any repo. saw hook — scan on clone and pull, before you install or build. |
| Audit | saw audit — host hygiene and branch-protection posture, stating plainly what it did not examine. |
| Advisories | saw db — an offline corpus for dependency CVE and malicious-package matching. |
Quick start
Prerequisites: Python 3.11+ — see docs/PREREQUISITES.md.
pip install stayawakebot
Or the latest from source:
pip install "stayawakebot @ git+https://github.com/Ndevu12/stayAwakeBot@main"
Then hunt. Each of these stands alone:
Scan the repository you are in:
saw scan
Or sweep every repository under a path:
saw scan ~/dev
Scan with your own allowlist, rather than the packaged defaults:
saw scan --config config/security.yml
Check the machine itself, not a repository — credential hygiene and start-up entries:
saw audit
New here? saw intro is a 60-second tour, and saw search "…" finds the command you want.
The distribution is published as
stayawakebot; the security CLI is the tersesawcommand (see the CLI guide).
Don't hand-maintain that workflow
Gate any repository's CI with one command — no install, no clone.
Every repository you own should refuse an infected merge. saw guard writes that GitHub Actions
workflow, keeps it pinned, and proves it is enforced — for one repository or a whole organisation.
Each command below stands alone; reach for the one you need.
Set it up. Writes the gate here, for you to review and commit:
saw guard setup
Or raise it as a pull request instead, which never pushes to main:
saw guard setup --pr
Check that a repository is actually guarded — present, SHA-pinned, current, and required:
saw guard check
Check a whole organisation, failing if any repository lacks a required gate:
saw guard check --org your-org -f
saw guard setup surgically pin-bumps a gate that already exists rather than replacing it, and
never clobbers a workflow installed by some other means. saw guard check goes further than "is the
file there" — it verifies branch protection actually requires the check, because a gate that is
not required is decoration.
Both sweep many repositories at once (--remote / --user / --org), like saw scan and
saw fix. See the CLI guide.
The workflow, by hand
A minimal equivalent, if you would rather write it yourself than run saw guard setup. The
installed file is not identical — it also carries a weekly pin-drift job and prefers a
GH_SECURITY_TOKEN secret — so run saw guard setup --dry-run to see exactly what it would write:
# .github/workflows/worm-scan.yml
name: Worm scan
on:
push:
branches: [main]
pull_request:
# Auto-remediation needs write access:
# contents: write -> push the security/auto-clean fix branch
# pull-requests: write -> open/update the rolling cleanup PR
# The scan itself only needs read; these are for the remediate step. Drop them
# both to `read` if you want detection without remediation.
permissions:
contents: write
pull-requests: write
jobs:
strix:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0 # full history is required
- uses: Ndevu12/strix@93fe465d7b0266c6010778999b73b591ae082f3e # v0.1.4
with:
version: '0.6.0' # pin the scanner too; blank tracks latest
# On an infected verdict, open ONE rolling `security/auto-clean` PR.
# The gate still goes RED until that PR is merged — remediation opens
# the fix, it does not make the check pass. Omit for detection only.
remediate: pr
About the pins
Ndevu12/strix ("StayAwakeBot Strix") is the public Action — a thin wrapper that installs the
published stayawakebot scanner from PyPI.
Pin every action by commit SHA, including this one: a tag can be moved to point at different code
after you have reviewed it, a SHA cannot. The trailing comment records which release the SHA
corresponds to, so the pin stays readable. In production pin version: as well — it selects the
stayawakebot release from PyPI and otherwise tracks whatever is latest at run time.
Other inputs: config-file to supply your own allowlist, fail-on to choose the verdict that fails
the build (default infected), and upload-sarif to send findings to code scanning.
See Security baseline.
Run via Docker (no local Python needed)
Prefer not to install a Python toolchain at all? Pull the image and scan a mounted repo:
docker run --rm -v "$PWD:/repo:ro" ghcr.io/ndevu12/stayawakebot \
saw scan /repo
The exit code is the verdict (0 clean, 1 findings). To keep the report file too, mount a
writable dir and run as your own user so the bind-mount is writable:
docker run --rm --user "$(id -u):$(id -g)" -v "$PWD:/repo" \
ghcr.io/ndevu12/stayawakebot \
saw scan /repo --reports-dir /repo/reports
Tags: :latest, :X.Y.Z, :X.Y, and :sha-<commit>. The image runs as a non-root user, is
built from the same wheel published to PyPI, and ships SLSA provenance + SBOM attestations.
Also in the package
stayawakebot additionally ships a health sentinel — a URL/uptime availability monitor
(HTTP status, latency, TLS and keyword checks) run with stayawake-health-check. It is
independent of saw and shares only the packaging. See
Usage and Configuration.
stayawake-health-check --config config/urls.yml
Documentation
- CLI command guide — the
sawsecurity commands (scan, fix, audit, guard, …) - Usage — install, run both bots, secrets, GitHub Actions, deploy your own
- Configuration & Reports — config file fields and report formats
- Credential hygiene — what a cached-credential finding means, and how to act on one safely
- Prerequisites — supported Python versions and install troubleshooting
- Security baseline — hardening checklist for any repo
- Contributing — development setup and guidelines
- Security policy — how to report a security issue privately
License
stayAwakeBot is dual-licensed:
- AGPL-3.0-or-later — free and open source. You must preserve attribution, and if you modify it and convey it or offer it over a network (e.g. as a hosted service), you must release your corresponding source under the AGPL too.
- Commercial license — a paid, proprietary-use option for closed-source or proprietary-SaaS use without the AGPL's source-disclosure obligations. For terms, contact saw@ndevuspace.com.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file stayawakebot-0.6.1.tar.gz.
File metadata
- Download URL: stayawakebot-0.6.1.tar.gz
- Upload date:
- Size: 1.1 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a701e69088583b9f06355dca9c2ed1bb6c2d2ea961f9b7cc85beae55ebd82430
|
|
| MD5 |
2211fc5bca54973c994729298237d483
|
|
| BLAKE2b-256 |
c5d7b369f9b01eada326e75fec7b75803696c5cedb8f2a099d25dfbad1c8f4ba
|
Provenance
The following attestation bundles were made for stayawakebot-0.6.1.tar.gz:
Publisher:
release.yml on Ndevu12/stayAwakeBot
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stayawakebot-0.6.1.tar.gz -
Subject digest:
a701e69088583b9f06355dca9c2ed1bb6c2d2ea961f9b7cc85beae55ebd82430 - Sigstore transparency entry: 2498665004
- Sigstore integration time:
-
Permalink:
Ndevu12/stayAwakeBot@440776f0fbd1c6348ce65d0da2b0b5a9c965dc13 -
Branch / Tag:
refs/tags/v0.6.1 - Owner: https://github.com/Ndevu12
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@440776f0fbd1c6348ce65d0da2b0b5a9c965dc13 -
Trigger Event:
push
-
Statement type:
File details
Details for the file stayawakebot-0.6.1-py3-none-any.whl.
File metadata
- Download URL: stayawakebot-0.6.1-py3-none-any.whl
- Upload date:
- Size: 1.2 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f4f68db8e69bd2480a31262370db678663fd2b00d981fff0314ebf4b4095d676
|
|
| MD5 |
07b0edac60684ff0223a8e89e01a3652
|
|
| BLAKE2b-256 |
de32bb6a339c4e14abe8f7c5fa347ed814e201cf3b3dcbce1e5e0581ab9f19fe
|
Provenance
The following attestation bundles were made for stayawakebot-0.6.1-py3-none-any.whl:
Publisher:
release.yml on Ndevu12/stayAwakeBot
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stayawakebot-0.6.1-py3-none-any.whl -
Subject digest:
f4f68db8e69bd2480a31262370db678663fd2b00d981fff0314ebf4b4095d676 - Sigstore transparency entry: 2498665028
- Sigstore integration time:
-
Permalink:
Ndevu12/stayAwakeBot@440776f0fbd1c6348ce65d0da2b0b5a9c965dc13 -
Branch / Tag:
refs/tags/v0.6.1 - Owner: https://github.com/Ndevu12
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@440776f0fbd1c6348ce65d0da2b0b5a9c965dc13 -
Trigger Event:
push
-
Statement type: