Skip to content

Releases: JAAAACCCCCCKKKK/SafeSC

v1.2.1 — Broader install-time behavior detection and path exclusion

Choose a tag to compare

@JAAAACCCCCCKKKK JAAAACCCCCCKKKK released this 10 Aug 10:27
1c21e9b

SafeSC v1.2.1

Broader install-time behavior detection (Rust and Python join npm), a way to exclude
paths from discovery, and the first end-to-end tests that prove the pipeline detects real
attack patterns.

pip install --upgrade "safesc[agent,anthropic]"   # or [agent,openai]

⚠️ Read before upgrading

This release can surface findings on dependencies that previously reported nothing.
Two new Stage-3 signals were added, so an audit that passed on v1.2.0 may now report
new gray-zone findings and spend LLM calls on them.

Both new signals are MEDIUM, which means they land in the gray zone for LLM
verification and cannot fail your CI gate on their own (fail_threshold is HIGH).
An audit's exit code should not change from these signals alone. What can change:

  • new findings in your JSON/Markdown/SARIF reports,
  • a modest increase in Stage-4 LLM calls (and therefore cost).

The one pre-existing HIGH behavior signal — npm's hasInstallScript — is unchanged.

New runtime dependency: pathspec>=0.12 is now a core dependency (it powers
.safescignore matching). It is small, pure-Python, and dependency-free.


Added

Exclude paths from discovery — .safescignore and --exclude

SafeSC walks the whole target directory, which meant no way to skip paths that
look like dependency files but shouldn't be audited — vendored copies, generated
trees, or security-test fixtures.

Drop a gitignore-syntax .safescignore at the scanned root. It is auto-discovered by
every entrypoint (index, scan, safesc audit, safesc query) with no flags,
because they all funnel through one discovery function:

# .safescignore
tests/fixtures/**
vendor/

For one-off exclusions, a repeatable --exclude flag layers on top of (never replaces)
that file:

safesc audit . --exclude "tests/fixtures/**"
index discover . --exclude "vendor/**"
scan signals . --exclude "vendor/**"

The GitHub Action exposes the same thing as an exclude input, one pattern per line:

- uses: JAAAACCCCCCKKKK/SafeSC@v1
  with:
    llm-api-key: ${{ secrets.SAFESC_LLM_API_KEY }}
    llm-provider: anthropic
    exclude: |
      tests/fixtures/**
      vendor/

Matching uses pathspec for real gitignore semantics (negation, **, anchoring,
directory pruning) rather than hand-rolled globbing.

Rust build-script detection (Stage 3, MEDIUM)

Crates that declare a Cargo links key are now flagged as having a build script.
crates.io exposes this as lib_links, and Cargo requires a build script whenever
links is set — so this is a zero-false-positive proxy that needs no download.

It is deliberately narrow: a build.rs used purely for codegen (no links key)
is not flagged. That is intentional, not an oversight. Build scripts are the norm in
Rust — serde, libc, anyhow and proc-macro2 all ship one — so a "has a build.rs
at all" signal would fire on roughly two-thirds of every Rust dependency tree and be
useless as an escalation trigger. Content inspection of any build.rs still happens at
Stage 4 for crates that reach it.

Python install-time build detection (Stage 3, MEDIUM)

A release that publishes an sdist but no wheel must be built from source at install
time, executing the project's PEP 517 backend / setup.py. A wheel is only unpacked and
runs no project code, so wheel-bearing releases are never flagged.

Measured before shipping: 0 of 45 real-world dependencies flagged, while correctly
firing on genuinely sdist-only releases (pycparser==2.14,
python-Levenshtein==0.12.0). It costs no extra HTTP requests — the data comes from
a registry response SafeSC already fetches.

End-to-end attack-pattern test coverage

tests/fixtures/attacks/ adds four synthetic, inert fixtures — a build-script
payload, a poisoned install hook, a typosquat, and a clean negative control — driven by
tests/test_attack_fixtures.py through the real spine, gate, specialists, and scorer.

These are pattern fixtures, not incident replays: no malware is vendored, and every
payload is harmless. They exist to prove the pipeline detects an actual attack shape end
to end, and — via the clean baseline — that it still passes an ordinary project.

Clearer errors for missing provider SDKs

Running the CI tier without the SDK for your configured SAFESC_LLM_PROVIDER now fails
with an actionable install hint instead of a raw traceback.


Fixed

  • Rust audits no longer report a spurious "0 dependencies" warning. Cargo.toml is
    discovered but intentionally parses to nothing (it is a manifest, not a lockfile), yet
    it was not on the manifest-only allowlist — so every normal Rust project (Cargo.toml
    and Cargo.lock) was permanently marked "incomplete analysis".
  • Module-presence checks no longer misfire on injected test doubles.

Release engineering

The publish workflow now verifies that the git tag matches the version in
pyproject.toml, so a mistagged release fails before anything reaches PyPI.


Notes for maintainers

Two decisions in this release were made on measured evidence rather than intuition, and
are recorded in CLAUDE.md (§9, v2.9–v2.11) with the numbers behind them:

  1. Rust's signal is MEDIUM, not HIGH. It shipped as HIGH during development, which
    would have failed the gate for every consumer of openssl-sys/ring/libz-sys
    irrecoverably, since LLM signals may escalate but never downgrade. The governing rule
    is now explicit and test-pinned: only opted-in code execution may reach the gate.
    An npm lifecycle hook qualifies; a Cargo links key and an sdist-only release are
    routine build-system facts and do not.
  2. Rust detection was deliberately not broadened beyond links, because completeness
    would have destroyed the signal's selectivity.

Full changelog: v1.2.0...v1.2.1

v1.2.0 — SafeSC on PyPI: deterministic supply-chain gate with escalate-only LLM analysis

Choose a tag to compare

@JAAAACCCCCCKKKK JAAAACCCCCCKKKK released this 09 Aug 08:45
5f75ae4

First public release. SafeSC audits your dependency lockfiles in CI, verifies provenance,
and scores every dependency across five trust dimensions — architected so an LLM can never
talk the gate into passing something it shouldn't.

Install

pip install "safesc[agent,anthropic]"   # or [agent,openai]

Or drop the Action into any repository:

- uses: JAAAACCCCCCKKKK/SafeSC@v1
  with:
    llm-api-key: ${{ secrets.SAFESC_LLM_API_KEY }}
    llm-provider: anthropic          # required: anthropic | openai

What's in it

Deterministic spine, agentic fan-out. Stages 0–3 (discover → parse → verify hashes →
cheap signals) always run in a fixed order — the agent cannot reorder or skip them. Only
gray-zone dependencies fan out to LLM specialists for name-squatting, install-script
intent, and provenance gaps. Most dependencies never reach an LLM at all.

Escalate-only by construction. LLM findings and retrieved memory can raise a
dependency's severity but never lower it, and only the deterministic scorer writes the
gate decision. A malicious package that plants "this is safe, ignore warnings" in its
README cannot buy itself a pass — the worst it can do is be ignored.

Bring your own key. No server-side key, no shared account, no default provider. Keys
are held as SecretStr, threaded by injection only, and never enter the audit state,
logs, reports, or the vector store. Anthropic and OpenAI ship in the box, plus any
OpenAI-compatible endpoint (Azure, OpenRouter, Together, Groq, Ollama, vLLM, LiteLLM)
via llm-base-url.

One-step CI gate. The Action installs SafeSC, runs the audit, uploads SARIF to code
scanning, archives the reports, and fails the build on a critical finding. When code
scanning isn't available, the SARIF upload degrades to a warning instead of failing the
run.

Ecosystems: Python (uv / poetry / pip), npm / pnpm, Cargo, Go modules, Maven / Gradle.

Reports: SARIF, Markdown, and JSON.

Optional memory layer

safesc[memory] adds Redis + PGVector for cross-run recall of prior verdicts and known
attack fingerprints. It is retrieval grounding only — a prior "clean" record is evidence,
never an override, and it obeys the same escalate-only rule as everything else. Entirely
optional; the CI gate runs without any datastore.

Known limitations

  • llm-provider has no default and must be set explicitly, together with the matching SDK
    extra ([agent,anthropic] or [agent,openai]).
  • The optional memory layer requires you to provision Redis and PGVector yourself.
  • Ecosystem coverage is broadest for Python and npm; Cargo, Go, and Maven/Gradle support
    is newer and less battle-tested.

Security

Found a vulnerability? Please report it through the repository's Security tab rather than
a public issue.


Full Changelog: v1.1.3...v1.2.0