Repository navigation
src2sink 1.0.0
Yanked on PyPI, 2026-07-29 — "Superseded by 1.0.1". Not a defect: 1.0.1 is
functionally identical, and this release is sound. Existing== 1.0.0pins
keep resolving as before; new installs resolve to 1.0.1 regardless.
First public release. src2sink builds a source-to-sink metabase: a structured,
human-readable knowledge base of an entire source-code estate, designed to be
loaded as context for LLM-assisted SAST so taint can be followed across
repositories — a SQL fragment built in one service and executed in another, an
internal library that silently forwards to a JDBC sink, PII entering at an
ingress and surfacing in a log three repos away.
Extraction
- Tree-sitter extractors for Java, Kotlin, Python, Go, JavaScript, and
TypeScript, plus configuration-file extraction (Springapplication.yml/
.properties, and friends) for facts that never appear in code. - Output schema v2 (
SCHEMA_VERSION = 2): a flow graph ofFlowNode
(source/propagator/sink/store, with afamily, a
pii_classification, adata_class, and a confidence rating) joined by
FlowEdgeat intra-file, intra-repo, and cross-repo scope. - Build-system and framework detection across Maven, Gradle, npm/yarn/pnpm, pip,
Poetry, Pipenv, Go modules, and Cargo. Declared dependencies are parsed from
pom.xml,build.gradle*, andpackage.json; component identity (which
repo publishes a given coordinate) additionally resolvespyproject.toml,
setup.cfg,Cargo.toml,composer.json,go.mod,*.csproj/*.fsproj/
*.vbproj, and*.gemspec. An internal-vs-external coordinate classifier
runs off your own namespace patterns.
Cross-repo analysis
- Taint catalogues — SQL sources and execution sinks, file sinks, outbound
HTTP sinks, PII sources and sinks, crypto operations, raw-code-payload
endpoints, and security-relevant configuration. - Graphs — service-call graph (HTTP out ↔ HTTP in), queue producer/consumer
graph, data-store graph, payload-endpoint producers, PII lifecycle, and
cross-repo phone-number flows. - OpenAPI discovery — specs found in the estate are matched to services and
folded into the service-call edges. - Phase 3 models — per-repo auth cards, crypto-agility cards, a PII lifecycle
model, and a GDPR Article 30 ROPA projection. - Bidirectional tracing —
src2sink-tracefollows a target repo or endpoint
upstream to its producers and downstream to its sinks;src2sink-trace-batch
does it for every raw-payload endpoint discovered.
Command-line tools
src2sink-build (extract + aggregate), src2sink-trace, src2sink-trace-batch,
src2sink-curate (internal-library taint tables), and src2sink-baseline
(fleet baseline). Builds are incremental by git SHA — unchanged repos are
skipped, cross-repo aggregation always re-runs so the estate stays consistent.
Built to scan hostile input
Scanned repositories are treated as untrusted, and the outputs are treated as
prompt material for an LLM. Every control below is traced to a test through the
SRTM:
- Execution bulkhead — each repo is analysed in its own process with a
wall-clock budget, so a pathological parse or catastrophic regex kills one
repo, not the run. - Path containment — crafted
.git/HEADsymrefs and escaping symlinks cannot
read outside the repo. - Hardened XML — manifests are parsed with
defusedxml; entity-expansion
payloads do not expand. - Size, file-count, and line-length caps, plus a content pre-screen that
skips binary and minified/obfuscated files before a parser sees them. Every
skip is recorded, never silent. - Untrusted-output neutralisation — extracted content is escaped and fenced
in Markdown and JSONL so a comment in a scanned repo cannot issue instructions
to a downstream LLM. - Literal-PII redaction in code snippets, and log hygiene that reports the
exception type and repo id rather than paths or content. - Run provenance — every build writes a secret-free
run-manifest.json
(tool version, per-repo SHAs, counts, UTC timestamps) for reproducibility and
Article 30 records.
Operational guidance — data classification, least-privilege CI, retention and
erasure — is in docs/operations-security.md;
the risk register is in docs/threat-model.md.
Quality gates
CI runs six gates on every push and pull request, and weekly:
| Gate | Enforces |
|---|---|
test |
302 tests, 84% coverage overall, 90% on the security-critical modules |
srtm |
every requirement in the SRTM still has an implementing test or a documented audit |
mypy (strict) |
mypy --strict, clean across the package and scripts |
bandit |
Python SAST, clean |
pip-audit |
no known advisories against the locked dependency set |
opengrep |
pattern SAST with a pinned ruleset, clean at ERROR severity |
Dependencies are hash-pinned in a committed uv.lock and installed with
uv sync --locked. Known false positives are annotated inline with a stated
reason rather than suppressed wholesale.
Fixed before release
SKIP_DIRSwas matched against each manifest's absolute path, so a repos
root under any colliding path segment —/tmp/repos,~/build/repos— indexed
nothing at all. Silently: no error, no note, every coordinate resolving to "not
found" and every source-map lookup coming back empty. The first CI run caught
it, because GitHub runners put pytest's temporary directories under/tmp;
reproducible locally withTMPDIR=/tmp.SKIP_DIRSnow applies only to
segments below the scan root — the absolute prefix is the operator's
filesystem layout, not part of the scanned tree. No published version is
affected: the fix predates this release.
Requirements
Python 3.14+. Install with pip install src2sink or uv add src2sink.
Known limitations
- Detection is heuristic and rated (
high/medium/low); treatlowas
investigate, not confirmed. - Scala files are counted in the language breakdown and get the language-agnostic
regex passes, but there is no tree-sitter grammar for them yet, so no AST-level
extraction. - The metabase is a concentrated map of weaknesses and personal-data locations.
Store it access-controlled and encrypted at rest — see the operations guide.