Skip to main content

fenceline

Zero-day security scanner for the Boti workspace.

Installation

uv add fenceline

Maps every finding to a CWE from the 2025 CWE Top 25, OWASP Top 10:2025, and known Python zero-day exploit patterns (pickle bypass CVE-2026-56315, PyYAML shadow vulnerability CVE-2026-24009, LangChain SSTI CVE-2025-68664, dependency confusion CVE-2025-61774, and more — see the CWE reference printed at the end of every text-mode report). 56 checks total: most are AST-based (won't false-positive on a dangerous call mentioned in a docstring or comment); the rest are line-regex checks for surface-syntax patterns that don't need full parsing.

Usage

uv run fenceline
uv run fenceline --json > report.json
uv run fenceline -q
uv run fenceline --fail-on critical
uv run fenceline --package my-lib=my-lib/src/my_lib

Exit codes: 0 (no findings at or above --fail-on), 1 (one or more).

Options

Flag Default Description
--json off Machine-readable output
--quiet / -q off Suppress the banner
--fail-on high Severity threshold (critical|high|medium|low|info) for the exit code
--confidence-min low Drop findings below this confidence (high|medium|low)
--package NAME=PATH Add or override a scan target package (repeatable)
--baseline PATH Only report/fail on findings not already present in this baseline
--write-baseline PATH Snapshot current findings to PATH and exit 0

By default, fenceline scans boti, boti-data, boti-dask, and itself.

Severity vs. confidence

Every finding carries two independent ratings, the same distinction Bandit makes: severity is how bad it would be if the finding is real; confidence is how sure the check is that it is real. An AST-based check that matched a real Call node is HIGH confidence; a line-regex check that can't fully rule out a docstring or string-literal mention is MEDIUM; a handful of inherently heuristic checks (timing-attack keyword proximity, ReDoS backtracking-risk judgment, debug=True pattern matching) are LOW. Use --confidence-min medium to cut noise from the fuzzier checks without raising your severity bar.

Baselining an existing codebase

Adopting fenceline on a codebase with existing findings doesn't mean fixing all of them before CI can pass:

uv run fenceline --write-baseline fenceline-baseline.json   # snapshot today's findings
uv run fenceline --baseline fenceline-baseline.json          # only new findings fail CI

A finding is matched against the baseline by (cwe_id, file, code_snippet), not line number, so it keeps matching across unrelated edits that shift line numbers elsewhere in the file. Re-run --write-baseline periodically (or whenever a baselined finding is deliberately fixed) to keep it current.

Suppressing a specific finding

# nosec (bare) suppresses every finding on that line; # nosec CWE-502 suppresses only that CWE, leaving any other finding on the same line intact — the same convention Bandit uses, with CWE IDs instead of Bandit's own B-numbers:

pickle.loads(data)  # nosec CWE-502 -- trusted internal cache, not user input

Extending fenceline with your own checks

Built-in checks aren't dynamically discovered — they're a fixed list. Your own checks are, via a fenceline.checks entry point in your own pyproject.toml:

[project.entry-points."fenceline.checks"]
"My Custom Check (CWE-000)" = "my_package.checks:my_check_function"

The entry point's own name becomes the check's display name; the object it points at must be a function with the same (path, lines, tree) -> list[Finding] signature every built-in check has. A plugin that fails to import is skipped with a warning rather than crashing the whole scan.

Design

  • fenceline.models — the Finding dataclass, severity ordering, and confidence ordering.
  • fenceline.config — workspace-root discovery and the default package registry.
  • fenceline.ast_helpers — shared AST/text-matching helpers used across checks.
  • fenceline.scanner — file discovery and reading.
  • fenceline.checks — the built-in check registry, plus fenceline.checks entry-point discovery for third-party checks.
  • fenceline.checks.ast_checks — checks that walk the parsed AST (won't match a dangerous call mentioned in a docstring or string literal).
  • fenceline.checks.text_checks — checks that scan raw source lines for surface-syntax patterns.
  • fenceline.checks.manifest_checks — checks over dependency manifests (pyproject.toml, requirements.txt, etc.), not Python source.
  • fenceline.suppression# nosec inline-suppression parsing.
  • fenceline.baseline — baseline snapshot/diff for adopting fenceline on an existing codebase.
  • fenceline.reporting — text/JSON report rendering.
  • fenceline.cli — argument parsing and the scan loop.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fenceline-1.0.2.tar.gz (41.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fenceline-1.0.2-py3-none-any.whl (40.6 kB view details)

Uploaded Python 3

File details

Details for the file fenceline-1.0.2.tar.gz.

File metadata

  • Download URL: fenceline-1.0.2.tar.gz
  • Upload date:
  • Size: 41.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for fenceline-1.0.2.tar.gz
Algorithm Hash digest
SHA256 8e7d08b43cc8b4708774ec06f7147052cb491f6fa4cfe2ddd8f0c67f785c4633
MD5 da55315deb530879a823056adffce8c6
BLAKE2b-256 0f90503149d499c0e291121fc8bc056bfe20d9f05e6b665ea916bbd9f0525f8a

See more details on using hashes here.

File details

Details for the file fenceline-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: fenceline-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 40.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for fenceline-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 aa396699ab9ef4d98676268ab8250086e5afb828bba5a75cfdd2da5d3bff2d58
MD5 4ab2d01c399652808d32e52b433630d9
BLAKE2b-256 b4c4d874a371811dee0f4c25d162cbf8726fdc39c2f9681af6806cf8e8b1980d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page