Skip to content

Security: Gavin-Borges/SESTRAV

SECURITY.md

Security Policy

Overview

SESTRAV is a solo-maintained, academic research project developed at the University of Rhode Island. There is one active maintainer. Please keep this in mind when setting expectations around response timelines.

Supported Versions

Only the latest major release of SESTRAV is actively supported with security updates.

Version Supported
2.0.x
< 2.0

Reporting a Vulnerability

Please do NOT open a public GitHub Issue for security vulnerabilities. Public disclosure before a fix is available puts all users at risk.

Preferred: Private Email Report

Send a confidential report to the maintainer directly:

Email: gavinmborges1104@gmail.com

Please include the following in your report:

  • Summary: A clear description of the vulnerability and its potential impact.
  • Reproduction steps: Step-by-step instructions to reproduce the issue, including any sample inputs, scripts, or configuration needed.
  • Environment: Python version, OS, relevant package versions.
  • Proposed fix (optional): Any remediation ideas or patches you have.

Alternative: GitHub Private Vulnerability Reporting

GitHub's built-in private reporting is also available:

  1. Navigate to the SESTRAV Security tab.
  2. Click "Report a vulnerability".
  3. Fill in the advisory form - this is end-to-end encrypted between you and the maintainer.

Response Commitment

As a solo-maintained project, the maintainer commits to:

  • Acknowledge receipt of your report within 3-5 business days.
  • Provide an initial assessment (severity, scope, reproducibility) within 10 business days of acknowledgement.
  • Coordinate a fix and disclosure timeline with you collaboratively.
  • Credit reporters in the release notes (unless you prefer anonymity).

Thank You

Responsible disclosure helps keep SESTRAV and its users safe. Thank you for taking the time to report vulnerabilities privately.

Maintainership & Continuity

SESTRAV is led by a single active maintainer; the v1 collaborators credited in CONTRIBUTORS.md are no longer active. There is no backup maintainer and no succession arrangement, and none is planned - GOVERNANCE.md and BUS_FACTOR.md record that honest status rather than a continuity plan.

The project therefore cannot survive the loss of the maintainer as a maintained project. What it does have is weaker but real: the source, full history and build procedure are public and MIT-licensed, so a third party may fork and continue the work independently. If you are assessing whether a security report will be actioned in the maintainer's absence, assume it will not be.

Account Security

All accounts with write access to this repository MUST have Two-Factor Authentication (2FA) enabled on GitHub. In practice that is one account, the sole maintainer's.

Release Integrity & Verification

Release artifacts are distributed via GitHub Releases over HTTPS and ship with a SHA-256 checksum manifest generated by src/release_bundle.py, which lets consumers verify integrity:

sha256sum -c <manifest-file>   # confirm the downloaded ZIP matches the manifest

Release authenticity (signing) is automated by .github/workflows/release.yml: pushing a version tag builds the distribution and produces a keyless SLSA build-provenance attestation (Sigstore, via GitHub OIDC - no maintainer-managed keys). From the release at which signing is introduced onward:

  • Tags are annotated but not yet signed (version_tags_signed, an OpenSSF SUGGESTED criterion, is currently Unmet - see docs/releasing.md). Once tag signing is introduced they will be verifiable with git tag -v vX.Y.Z.

  • Artifacts carry a Sigstore provenance attestation, verifiable with:

    gh attestation verify sestrav-<version>-py3-none-any.whl --repo Gavin-Borges/SESTRAV

The full release procedure is documented in docs/releasing.md.

Maintainer note: artifact attestation is keyless, so there is no artifact-signing key to record. When tag signing is introduced, note the first signed version here and record the signing identity per BUS_FACTOR.md.


Personally Identifiable Information (PII) & Patient Health Data (PHI)

SESTRAV is designed as a standalone, offline bioinformatics pipeline.

  • Data Privacy by Design: All scoring and feature extraction processes execute strictly on the local machine or host environment. SESTRAV does not transmit, upload, or collect any sequence data, user parameters, or predictive outputs.
  • No PII/PHI Requirement: The pipeline accepts standard FASTA, CSV, and YAML configurations. It does not require, accept, or process personally identifiable information (PII) or protected health information (PHI). Users are cautioned against introducing patient metadata or identifying fields into sequence inputs.
  • Credential Safety: The pipeline does not connect to external patient databases and has no credential store or public telemetry APIs.

Vulnerability Triage & Remediation Policy

How findings from Dependabot, code scanning, and the CI security workflows are prioritized and acted on. The policy is deliberately two-tiered, but "blocking" here means one specific, verifiable thing: gating the merge button via the Protect Main Branch ruleset (id 16846770) - its four required status checks (test (3.13), Require human review, check_dco, Cited commits resolve) plus its code-scanning rule, which names CodeQL only. A tool that is not on either of those two lists cannot block a merge, even if it fails its own CI job red.

Severity -> action (target SLA)

Severity Action Target SLA Gate
Critical Patch or documented mitigation before the next merge to main 48 hours Blocking
High Patch or mitigation within the current release cycle 7 days Blocking
Medium Scheduled fix; risk-acceptable with written justification 30 days Advisory
Low Best-effort; batched with routine dependency updates Next cycle Advisory

Timelines follow the solo-maintainer cadence described under Response Commitment above. The Severity column states intent; the CI gate map below states what is mechanically enforced today, and the two are not yet the same for every row.

CI gate map

Tool / workflow What it checks Tier
Bandit (security.yml, -ll) Python SAST, MEDIUM+ severity Advisory - fails its own CI job on a finding, but is not a required status check, so it does not gate the merge button
Dependency Review (dependency-review.yml) New deps introduced in a PR (fail-on-severity: moderate) Advisory - fails its own CI job on a finding, but is not a required status check, so it does not gate the merge button
CodeQL (security.yml) Deep SAST -> Security > Code scanning Blocking - the ruleset's code-scanning rule names CodeQL
Semgrep (security.yml) SAST ruleset p/python -> Security > Code scanning Advisory
pip-audit (security.yml, weekly + PR) CVEs in the pinned requirements.lock -> run summary Advisory - fails closed (non-continue-on-error) on any lockfile advisory absent from environments/accepted_advisories.toml, but is not a required status check
Dependabot alerts Known CVEs in dependencies -> Security > Dependabot Advisory (triaged)

Advisory findings never block a merge on their own - none of them is a required status check or a code-scanning rule on Protect Main Branch. Three of them (Bandit, Dependency Review, pip-audit) still turn their own CI job red on a finding rather than merely reporting one; that failure is visible in the run and in branch-status UI, but does not prevent the merge button from going green. Findings surface as (a) tracked, dismissable alerts in Security > Code scanning (Semgrep/CodeQL SARIF), (b) a red or markdown-annotated security.yml job run (Bandit, Dependency Review, pip-audit), and (c) Dependabot alerts/PRs. They are reviewed on the weekly cadence and logged in the register below whenever consciously deferred.

Recording a risk acceptance

When a finding is intentionally left unfixed (no upstream patch, not reachable in SESTRAV's offline model, etc.):

  1. Add an entry to the Risk-Acceptance Register below: identifier, component, rationale, and a re-review trigger or date.
  2. If it is noisy in CI, suppress it at the source with the verified advisory ID and a pointer back to this register entry:
    • For a pip-audit finding on environments/requirements.lock (the production lockfile), add an entry to environments/accepted_advisories.toml - .github/workflows/security.yml's pip-audit job fails closed on any finding not listed there (tools/check_lockfile_advisories.py). Do not use pip-audit's --ignore-vuln flag for this: it is applied before pip-audit writes its own report, so a suppressed finding becomes invisible to every tool that reads that report, including the ones meant to notice when it gets fixed upstream. That gap is exactly how CVE-2025-3000 sat patched-but-unnoticed for four weeks; see the entry below.
    • For anything else (Semgrep/CodeQL SARIF, Dependabot), dismiss-with-reason in the Security tab, citing this register entry in the dismissal comment.
    • Never suppress without a register entry.
  3. Note material changes in the CHANGELOG.md Unreleased / Security section as the audit trail.

Risk-Acceptance Register & Upstream Mitigations

As a standalone, offline scientific tool, SESTRAV occasionally relies on complex third-party libraries (e.g., PyTorch) that may contain upstream vulnerabilities with no available vendor patch. Each consciously-deferred advisory is logged here.

  • CVE-2025-3000 (PyTorch JIT script memory corruption): RESOLVED 2026-08-05 - no longer risk-accepted. PyTorch <= 2.12.1 contains a memory corruption flaw inside torch.jit.script.

    • Identifiers: CVE-2025-3000, GHSA-rrmf-rvhw-rf47, PYSEC-2025-194.
    • Resolution: upgraded to torch==2.13.0, the first patched release (published 2026-07-08), across requirements.in, requirements.txt, environments/requirements.lock and environments/requirements-ci-torch-cpu.txt, plus a torch>=2.13.0 floor in pyproject.toml. The lockfiles only govern hash-pinned installs; the pyproject.toml floor is what stops pip install -e . or a downstream consumer from resolving back into the affected range (<= 2.12.1).
    • How this entry went stale, recorded deliberately: it asserted "No upstream patch is available" and set a re-review trigger of "publication of a patched release." That trigger fired on 2026-07-08 and went unnoticed for four weeks, because nothing re-evaluates this register on a schedule - the suppression was set once and never revisited. The advisory's own metadata (firstPatchedVersion: 2.13.0) was the authoritative signal and was queryable the whole time.
    • Suppressions removed: the --ignore-vuln PYSEC-2025-194 / GHSA-rrmf-rvhw-rf47 flags have been deleted from .github/workflows/security.yml, so pip-audit now reports this advisory again if it ever reappears. Any previously dismissed Dependabot alerts for it are superseded by the upgrade.
    • Side effect, intentional: torch 2.12.0 declared a setuptools<82 build-metadata cap that collided with this repo's setuptools>=83.0.0 floor (GHSA-h35f-9h28-mq5c) and forced every lockfile recompile through a uv override file. torch 2.13.0 declares setuptools>=77.0.3, so overrides.txt has been retired and both application lockfiles now resolve unaided. The setuptools floor itself is unchanged - it was the security constraint, not the workaround.
  • Standing lesson from the entry above: a risk acceptance with no scheduled re-review is a claim that decays silently. Every live entry in this register carries a re-review trigger; none of them fire on their own. (Writing this sentence exposed a counter-example: the -W error bypass below had no trigger at all. One was added rather than weakening the rule.) Re-check this register whenever dependencies are audited, and treat an advisory's firstPatchedVersion field as the authoritative test of "is a patch available yet," not the prose in this file.

  • Semgrep dangerous-subprocess-use-tainted-env-args (external-tool wrappers): Risk-accepted false positive. The benchmark wrappers shell out to external binaries (PRIME, PredIG/Docker) via subprocess.run.

    • Identifiers: Semgrep python.lang.security.audit.dangerous-subprocess-use-tainted-env-args, Bandit B603.
    • Scope: scripts/run_prime_wrapper.py, scripts/run_predig_wrapper.py, scripts/run_predig_batched.py.
    • Severity: Error (Semgrep default); not exploitable in SESTRAV's model.
    • Mitigation: All calls use the list/argv form (shell=False), so no shell interpretation occurs. Command arguments are sourced from the operator's local CLI (argparse) and constant literals - never from untrusted or network input.
    • Suppression: An inline # nosemgrep on all three call sites, and a CI step that makes GitHub honour it. Corrected 2026-08-17: this entry previously stated that an inline # nosemgrep does not clear the taint-mode finding, and that Security-tab dismissal was therefore the mechanism. The marker does clear it - the scanner's own verdict is Ran 154 rules on 164 files: 0 findings. What did not happen was GitHub acting on it: semgrep still wrote each finding into the SARIF tagged "suppressions": [{"state": "accepted"}], and code scanning ingested it regardless, so the alerts were held closed only by manual dismissals that a single edit to the suppressed line could invalidate. security.yml now drops suppressed results before the SARIF upload, so the uploaded analysis matches the scan and no per-alert dismissal is required. Unsuppressed findings are untouched and still raise alerts. See docs/security_compliance.md for the measurement.
    • Re-review trigger: if any wrapper begins accepting subprocess arguments from untrusted/remote input, or switches to shell=True.
  • MCP SDK transport vulnerabilities (session hijacking / origin validation): Risk-accepted. The mcp package is pulled in transitively as a dev/CI dependency of semgrep; it is not an application dependency and no MCP server transport is ever invoked by SESTRAV.

    • Identifiers: GHSA-vj7q-gjh5-988w, GHSA-jpw9-pfvf-9f58, GHSA-hvrp-rf83-w775.
    • Scope: mcp (transitive, via semgrep) in the CI/dev dependency closure.
    • Severity: High (per GitHub advisory). All three are server-transport vulnerabilities (session-ID authentication bypass, WebSocket origin validation); SESTRAV never starts an MCP server, so the vulnerable code path is unreachable.
    • Mitigation: Not used. SESTRAV imports mcp only as a side effect of installing semgrep for CI static analysis; no mcp.server.* transport is constructed or run anywhere in the codebase or CI.
    • Suppression: Dependabot alerts dismissed as not_used. This entry is the authoritative record; the dismissal comment on each alert points back here.
    • Re-review trigger: if mcp ever becomes a direct/runtime dependency, or an MCP server transport is added to the codebase.
  • Strict Warning Enforcement (-W error bypass) for PyTorch Ecosystem: Risk-accepted. OpenSSF Silver requires strict compiler/linter warnings to be enabled and addressed.

    • Scope: pytest test collection and runtime.
    • Rationale: Deep dependencies within torch_geometric raise unpatchable DeprecationWarnings during import (e.g., torch_geometric.distributed deprecation since 2.7.0). Enforcing -W error globally breaks the CI pipeline.
    • Mitigation: We enforce strict warnings exclusively through our linting pipeline (Ruff) which is set to fail on any warning or error, satisfying the Silver criteria for static analysis, while allowing runtime PyTorch deprecation warnings to pass in test execution.
    • Re-review trigger: any torch or torch-geometric major/minor upgrade (which may drop the import-time DeprecationWarnings this bypass exists for), or any narrowing of the pytest warning filter. Added 2026-08-05: this entry previously carried no trigger, which the standing lesson above says is exactly how a risk acceptance decays silently.

There aren't any published security advisories