Skip to content

feat(cli): background update check with pip-style notice#124

Merged
eFAILution merged 1 commit intofeat/argus-portabilityfrom
feat/cli-update-check
May 6, 2026
Merged

feat(cli): background update check with pip-style notice#124
eFAILution merged 1 commit intofeat/argus-portabilityfrom
feat/cli-update-check

Conversation

@eFAILution
Copy link
Copy Markdown
Collaborator

Description

Surfaces a soft notice when a newer argus release is available on PyPI — a security tool that's months out of date is missing CVE-database updates, new severity-classification rules, and bug fixes that affect scan correctness. Mirrors the pattern users already know from pip, gh, uv, etc.

Changes Made

  • Added new scanner/workflow
  • Modified existing scanner/workflow
  • Updated documentation
  • Fixed bug
  • Other (please specify)

Details

New module: argus/update_check.py

Polls PyPI (https://pypi.org/pypi/argus-security/json) once per day per machine, caches the result in ~/.cache/argus/update-check.json (XDG_CACHE_HOME aware), compares against the installed version, prints a pip-style notice at end-of-command when a newer release exists.

[notice] A new release of argus-security is available: 0.7.2 → 0.8.1
[notice] To update, run: pip install --upgrade argus-security

Five non-negotiable design constraints:

Constraint Implementation
Air-gap friendly Every URLError/OSError/JSON-parse failure is silenced. No timeouts, no errors, no slowdown.
Privacy-respectful One HTTP request per machine per 24h. Same data PyPI already gets from pip install argus-security.
Zero scan-latency cost Runs in a daemon thread alongside the scan; result is consumed at end-of-command. Scans take seconds-to-minutes; the check completes in well under 500ms in parallel.
Override-friendly Three suppression hooks (env var, flag, --quiet) plus a custom URL for TestPyPI / private mirrors. Editable / dev / RC installs auto-skip.
Pre-release-aware 0.7.2.dev0+g123abc, 0.7.2rc1, 0.7.2+dirty all skip — contributors on bleeding-edge don't get told a release version is "available."

Suppression hierarchy (most persistent first):

  1. ARGUS_NO_UPDATE_CHECK=1 env var (set once for CI / air-gap)
  2. --no-update-check flag (per-invocation)
  3. --quiet flag (auto-respects explicit silence)
  4. Dev install detection (__version__ contains .dev / + / rc)
  5. Network errors (silent)

Custom registries:

# Argus maintainers validating a TestPyPI release before promotion
ARGUS_UPDATE_CHECK_URL=https://test.pypi.org/pypi/argus-security/json argus scan

# Air-gapped enterprise with a private mirror
ARGUS_UPDATE_CHECK_URL=https://pypi.internal.corp/pypi/argus-security/json argus scan

Default is hardcoded to public PyPI because auto-detecting which index argus was installed from is unreliable in Python — pip doesn't preserve that metadata.

CLI integration in _cmd_source_scan and _cmd_container_scan:

  • start_background_check(args) after argparse + manifest creation
  • update_check.notice() consumed right before return exit_code so the notice never hides scan results behind version chatter

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing performed
  • Tested with different scanner combinations

Test Results

38 new unit tests in argus/tests/test_update_check.py covering:

  • Suppression hooks: every flag/env var combo, dev install patterns
  • URL resolution: default, env var override
  • Fetch error paths: URLError, OSError, malformed JSON, missing version field — all return None
  • Cache logic: fresh cache, stale cache (refetch), no cache, corrupt cache (recovery)
  • Version comparison: newer/same/older
  • Notice formatting: includes both versions, pip upgrade command, [notice] prefix
  • Background thread: exception swallowing, notice retrieval

End-to-end smoke test: ARGUS_UPDATE_CHECK_URL=https://invalid.example.test/foo.json argus scan ... produced no [notice] block and exited cleanly — air-gap contract holds.

Full SDK suite: 1628 passing (was 1590; +38).

Security Considerations

  • No security impact
  • Security enhancement
  • Potential security implications (explain below)

Security Details

This is the security-tool-imperative completion. Outdated security tools miss CVE coverage, severity-rule updates, and bug fixes. The notice points users at the upgrade path so a security-conscious team isn't running an argus release that's silently 6 months behind.

The implementation itself is conservative: 24h cache, 2-second HTTP timeout, no telemetry beyond the version-fetch GET, no execution of fetched data. Air-gap support via env var plus silent network-failure handling.

AI Context Updates (.ai/)

  • N/A — additive feature, no architecture changes

Checklist

  • Code follows project style guidelines
  • Documentation updated (CLI reference regenerated)
  • Changelog updated (if applicable)
  • All tests pass
  • Reviewed by at least one maintainer
  • Reviewed CONTRIBUTING.md guidelines

For New Scanners/Actions (if applicable)

  • N/A — CLI feature, no scanner changes

A security tool that's months out of date is missing CVE-database
updates, new severity-classification rules, and bug fixes that affect
scan correctness. argus now polls PyPI once per day per machine,
caches the result for 24h, and prints a pip-style notice at the end
of long-running commands when a newer release exists.

Design constraints:

* Air-gap friendly. Every network failure (URLError, OSError, JSON
  parse, missing field) is silenced — air-gapped CI runners and
  offline machines never see a slowdown, error, or visible failure
  because PyPI is unreachable.
* Privacy-respectful. One HTTP request per machine per 24h, cached
  in ~/.cache/argus/update-check.json (or XDG_CACHE_HOME). The same
  data PyPI already gets from pip install argus-security.
* Zero scan-latency cost. Runs in a daemon thread alongside the
  scan; the result is consumed at end-of-command. Scans typically
  take seconds to minutes; the update check completes in well under
  500ms and runs in parallel.
* Override-friendly:
  - ARGUS_NO_UPDATE_CHECK env var disables the check entirely
    (set this in CI / air-gapped environments).
  - --no-update-check flag for per-invocation control.
  - --quiet auto-respects explicit silence.
  - ARGUS_UPDATE_CHECK_URL points at TestPyPI or private mirrors
    when the user knows their distribution channel differs from
    public PyPI.
  - Editable / dev / RC installs auto-skip — a contributor on
    0.7.2.dev0+g123abc isn't told 0.7.2 is "available."

Notice shape matches pip's so it reads familiar:

    [notice] A new release of argus-security is available: 0.7.2 → 0.8.1
    [notice] To update, run: pip install --upgrade argus-security

Wired into _cmd_source_scan and _cmd_container_scan: start the
background check after argparse and manifest creation, consume right
before return exit_code so the notice never hides scan results
behind version chatter.

New module argus/update_check.py with 38 unit tests covering
suppression hooks, URL resolution, every error path returning None,
cache freshness, version comparison, notice formatting, and
background-thread exception swallowing.

End-to-end smoke test: argus scan with ARGUS_UPDATE_CHECK_URL set
to an unreachable host produced no [notice] block and exited
cleanly (air-gap contract).

CLI reference regenerated for the new flag.

Tests: 1628 passing (was 1590; +38).
@codecov
Copy link
Copy Markdown

codecov Bot commented May 6, 2026

Codecov Report

❌ Patch coverage is 95.11401% with 15 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
argus/update_check.py 93.04% 8 Missing ⚠️
argus/cli.py 46.15% 7 Missing ⚠️

📢 Thoughts on this report? Let us know!

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 6, 2026

🔒 Argus Container Security Scan

Branch: feat/cli-update-check
Commit: 9dcc7da

📊 Combined Findings Summary

🚨 Critical ⚠️ High 🟡 Medium 🔵 Low 📦 Total 🔢 Unique
1 22 67 64 154 154

Scanned: 4 containers | Build Failures: 0

📦 Container Breakdown

Container Image 🚨 Crit ⚠️ High 🟡 Med 🔵 Low Total Unique Status
cli ghcr.io/huntridge-labs/argus/cli:9dcc7daeabd18dd3fa4bdf1aaa08b876aeed043c 1 11 16 1 29 29
scanner-bandit ghcr.io/huntridge-labs/argus/scanner-bandit:9dcc7daeabd18dd3fa4bdf1aaa08b876aeed043c 0 0 2 0 2 2
scanner-opengrep ghcr.io/huntridge-labs/argus/scanner-opengrep:9dcc7daeabd18dd3fa4bdf1aaa08b876aeed043c 0 7 44 63 114 114
scanner-supply-chain ghcr.io/huntridge-labs/argus/scanner-supply-chain:9dcc7daeabd18dd3fa4bdf1aaa08b876aeed043c 0 4 5 0 9 9

🔍 Detailed Findings by Container

🚨 cli - 29 vulnerabilities (23 unique)

Image: ghcr.io/huntridge-labs/argus/cli:9dcc7daeabd18dd3fa4bdf1aaa08b876aeed043c

Combined (Deduplicated)

🚨 Critical ⚠️ High 🟡 Medium 🔵 Low Total Unique
1 11 16 1 29 23
🔷 Trivy Scanner (29 findings, 23 unique)
CVE Severity Package Version Fixed
CVE-2025-68121 🚨 CRITICAL stdlib v1.24.11 1.24.13, 1.25.7, 1.26.0-rc.3
CVE-2026-32280 ⚠️ HIGH stdlib v1.26.1 1.25.9, 1.26.2
CVE-2026-32281 ⚠️ HIGH stdlib v1.26.1 1.25.9, 1.26.2
CVE-2026-32283 ⚠️ HIGH stdlib v1.26.1 1.25.9, 1.26.2
CVE-2026-33810 ⚠️ HIGH stdlib v1.26.1 1.26.2
CVE-2025-61726 ⚠️ HIGH stdlib v1.24.11 1.24.12, 1.25.6
CVE-2025-61728 ⚠️ HIGH stdlib v1.24.11 1.24.12, 1.25.6
CVE-2026-25679 ⚠️ HIGH stdlib v1.24.11 1.25.8, 1.26.1
CVE-2026-32280 ⚠️ HIGH stdlib v1.24.11 1.25.9, 1.26.2
CVE-2026-32281 ⚠️ HIGH stdlib v1.24.11 1.25.9, 1.26.2
CVE-2026-32283 ⚠️ HIGH stdlib v1.24.11 1.25.9, 1.26.2
CVE-2026-34040 ⚠️ HIGH github.com/docker/docker v28.5.2+incompatible 29.3.1
CVE-2026-3219 🟡 MEDIUM pip 26.0.1 N/A
CVE-2026-6357 🟡 MEDIUM pip 26.0.1 26.1
CVE-2026-32282 🟡 MEDIUM stdlib v1.26.1 1.25.9, 1.26.2
CVE-2026-32288 🟡 MEDIUM stdlib v1.26.1 1.25.9, 1.26.2
CVE-2026-32289 🟡 MEDIUM stdlib v1.26.1 1.25.9, 1.26.2
CVE-2025-11579 🟡 MEDIUM github.com/nwaples/rardecode/v2 v2.1.0 2.2.0
CVE-2025-58058 🟡 MEDIUM github.com/ulikunitz/xz v0.5.12 0.5.15
CVE-2025-47914 🟡 MEDIUM golang.org/x/crypto v0.35.0 0.45.0
CVE-2025-58181 🟡 MEDIUM golang.org/x/crypto v0.35.0 0.45.0
CVE-2025-61730 🟡 MEDIUM stdlib v1.24.11 1.24.12, 1.25.6
CVE-2026-27142 🟡 MEDIUM stdlib v1.24.11 1.25.8, 1.26.1
CVE-2026-32282 🟡 MEDIUM stdlib v1.24.11 1.25.9, 1.26.2
CVE-2026-32288 🟡 MEDIUM stdlib v1.24.11 1.25.9, 1.26.2
CVE-2026-32289 🟡 MEDIUM stdlib v1.24.11 1.25.9, 1.26.2
CVE-2026-33997 🟡 MEDIUM github.com/docker/docker v28.5.2+incompatible 29.3.1
CVE-2026-41506 🟡 MEDIUM github.com/go-git/go-git/v5 v5.17.2 5.18.0
CVE-2026-27139 🔵 LOW stdlib v1.24.11 1.25.8, 1.26.1
⚓ Grype Scanner (0 findings, 0 unique)

✅ No vulnerabilities detected by Grype

🟡 scanner-bandit - 2 vulnerabilities (2 unique)

Image: ghcr.io/huntridge-labs/argus/scanner-bandit:9dcc7daeabd18dd3fa4bdf1aaa08b876aeed043c

Combined (Deduplicated)

🚨 Critical ⚠️ High 🟡 Medium 🔵 Low Total Unique
0 0 2 0 2 2
🔷 Trivy Scanner (2 findings, 2 unique)
CVE Severity Package Version Fixed
CVE-2026-3219 🟡 MEDIUM pip 26.0.1 N/A
CVE-2026-6357 🟡 MEDIUM pip 26.0.1 26.1
⚓ Grype Scanner (0 findings, 0 unique)

✅ No vulnerabilities detected by Grype

⚠️ scanner-opengrep - 114 vulnerabilities (50 unique)

Image: ghcr.io/huntridge-labs/argus/scanner-opengrep:9dcc7daeabd18dd3fa4bdf1aaa08b876aeed043c

Combined (Deduplicated)

🚨 Critical ⚠️ High 🟡 Medium 🔵 Low Total Unique
0 7 44 63 114 50
🔷 Trivy Scanner (114 findings, 49 unique)
CVE Severity Package Version Fixed
CVE-2026-4878 ⚠️ HIGH libcap2 1:2.75-10+b8 N/A
CVE-2025-69720 ⚠️ HIGH libncursesw6 6.5+20250216-2 N/A
CVE-2026-29111 ⚠️ HIGH libsystemd0 257.9-1~deb13u1 N/A
CVE-2025-69720 ⚠️ HIGH libtinfo6 6.5+20250216-2 N/A
CVE-2026-29111 ⚠️ HIGH libudev1 257.9-1~deb13u1 N/A
CVE-2025-69720 ⚠️ HIGH ncurses-base 6.5+20250216-2 N/A
CVE-2025-69720 ⚠️ HIGH ncurses-bin 6.5+20250216-2 N/A
CVE-2026-27456 🟡 MEDIUM bsdutils 1:2.41-5 N/A
CVE-2026-3184 🟡 MEDIUM bsdutils 1:2.41-5 N/A
CVE-2026-27456 🟡 MEDIUM libblkid1 2.41-5 N/A
CVE-2026-3184 🟡 MEDIUM libblkid1 2.41-5 N/A
CVE-2026-4046 🟡 MEDIUM libc-bin 2.41-12+deb13u2 N/A
CVE-2026-4437 🟡 MEDIUM libc-bin 2.41-12+deb13u2 N/A
CVE-2026-4438 🟡 MEDIUM libc-bin 2.41-12+deb13u2 N/A
CVE-2026-5435 🟡 MEDIUM libc-bin 2.41-12+deb13u2 N/A
CVE-2026-5450 🟡 MEDIUM libc-bin 2.41-12+deb13u2 N/A
CVE-2026-5928 🟡 MEDIUM libc-bin 2.41-12+deb13u2 N/A
CVE-2026-6238 🟡 MEDIUM libc-bin 2.41-12+deb13u2 N/A
CVE-2026-4046 🟡 MEDIUM libc6 2.41-12+deb13u2 N/A
CVE-2026-4437 🟡 MEDIUM libc6 2.41-12+deb13u2 N/A
CVE-2026-4438 🟡 MEDIUM libc6 2.41-12+deb13u2 N/A
CVE-2026-5435 🟡 MEDIUM libc6 2.41-12+deb13u2 N/A
CVE-2026-5450 🟡 MEDIUM libc6 2.41-12+deb13u2 N/A
CVE-2026-5928 🟡 MEDIUM libc6 2.41-12+deb13u2 N/A
CVE-2026-6238 🟡 MEDIUM libc6 2.41-12+deb13u2 N/A
CVE-2026-27456 🟡 MEDIUM liblastlog2-2 2.41-5 N/A
CVE-2026-3184 🟡 MEDIUM liblastlog2-2 2.41-5 N/A
CVE-2026-34743 🟡 MEDIUM liblzma5 5.8.1-1 N/A
CVE-2026-27456 🟡 MEDIUM libmount1 2.41-5 N/A
CVE-2026-3184 🟡 MEDIUM libmount1 2.41-5 N/A
CVE-2026-27456 🟡 MEDIUM libsmartcols1 2.41-5 N/A
CVE-2026-3184 🟡 MEDIUM libsmartcols1 2.41-5 N/A
CVE-2026-40225 🟡 MEDIUM libsystemd0 257.9-1~deb13u1 N/A
CVE-2026-40226 🟡 MEDIUM libsystemd0 257.9-1~deb13u1 N/A
CVE-2026-4105 🟡 MEDIUM libsystemd0 257.9-1~deb13u1 N/A
CVE-2026-40225 🟡 MEDIUM libudev1 257.9-1~deb13u1 N/A
CVE-2026-40226 🟡 MEDIUM libudev1 257.9-1~deb13u1 N/A
CVE-2026-4105 🟡 MEDIUM libudev1 257.9-1~deb13u1 N/A
CVE-2026-27456 🟡 MEDIUM libuuid1 2.41-5 N/A
CVE-2026-3184 🟡 MEDIUM libuuid1 2.41-5 N/A
CVE-2026-27456 🟡 MEDIUM login 1:4.16.0-2+really2.41-5 N/A
CVE-2026-3184 🟡 MEDIUM login 1:4.16.0-2+really2.41-5 N/A
CVE-2026-27456 🟡 MEDIUM mount 2.41-5 N/A
CVE-2026-3184 🟡 MEDIUM mount 2.41-5 N/A
CVE-2026-5958 🟡 MEDIUM sed 4.9-2 N/A
CVE-2026-5704 🟡 MEDIUM tar 1.35+dfsg-3.1 N/A
CVE-2026-27456 🟡 MEDIUM util-linux 2.41-5 N/A
CVE-2026-3184 🟡 MEDIUM util-linux 2.41-5 N/A
CVE-2026-27171 🟡 MEDIUM zlib1g 1:1.3.dfsg+really1.3.1-1+b1 N/A
CVE-2026-3219 🟡 MEDIUM pip 26.0.1 N/A

...and 64 more

⚓ Grype Scanner (0 findings, 0 unique)

✅ No vulnerabilities detected by Grype

⚠️ scanner-supply-chain - 9 vulnerabilities (9 unique)

Image: ghcr.io/huntridge-labs/argus/scanner-supply-chain:9dcc7daeabd18dd3fa4bdf1aaa08b876aeed043c

Combined (Deduplicated)

🚨 Critical ⚠️ High 🟡 Medium 🔵 Low Total Unique
0 4 5 0 9 9
🔷 Trivy Scanner (9 findings, 9 unique)
CVE Severity Package Version Fixed
CVE-2026-32280 ⚠️ HIGH stdlib v1.26.1 1.25.9, 1.26.2
CVE-2026-32281 ⚠️ HIGH stdlib v1.26.1 1.25.9, 1.26.2
CVE-2026-32283 ⚠️ HIGH stdlib v1.26.1 1.25.9, 1.26.2
CVE-2026-33810 ⚠️ HIGH stdlib v1.26.1 1.26.2
CVE-2026-3219 🟡 MEDIUM pip 26.0.1 N/A
CVE-2026-6357 🟡 MEDIUM pip 26.0.1 26.1
CVE-2026-32282 🟡 MEDIUM stdlib v1.26.1 1.25.9, 1.26.2
CVE-2026-32288 🟡 MEDIUM stdlib v1.26.1 1.25.9, 1.26.2
CVE-2026-32289 🟡 MEDIUM stdlib v1.26.1 1.25.9, 1.26.2
⚓ Grype Scanner (0 findings, 0 unique)

✅ No vulnerabilities detected by Grype


Generated by Argus

@eFAILution eFAILution merged commit 3ccf528 into feat/argus-portability May 6, 2026
22 checks passed
@eFAILution eFAILution deleted the feat/cli-update-check branch May 6, 2026 16:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant