BioETL integrates with the following public APIs:
- ChEMBL — Bioactivity data from scientific literature
- PubChem — Chemical compound information
- UniProt — Protein sequence and functional data
- PubMed — Scientific publication metadata
| Boundary | Risk Level | Mitigation |
|---|---|---|
| API responses | Medium | Schema validation, input sanitization |
| User configuration | Low | YAML schema validation |
| Local filesystem | Low | Path traversal prevention |
| Vector | Description | Mitigation |
|---|---|---|
| API Injection | Malformed data in API responses | Strict schema validation at adapter level |
| Data Poisoning | Corrupted or malicious records | Multi-layer validation (Bronze → Silver → Gold) |
| Secrets Exposure | Credentials in logs/code/VCR cassettes | Environment variables, sanitization hooks |
| Dependency Vulnerabilities | CVEs in third-party packages | pip-audit, OSV-Scanner, Dependency review, Dependabot |
All secrets MUST follow the naming convention:
BIOETL_{PROVIDER}_{KEY}
Examples (names wired in configs/providers/*.yaml / .env.example):
BIOETL_UNIPROT_API_KEYBIOETL_OPENALEX_API_KEYBIOETL_PUBMED_API_KEYBIOETL_SEMANTICSCHOLAR_API_KEY
| Requirement | Status |
|---|---|
Never commit .env files |
✅ Enforced via .gitignore |
| VCR cassettes sanitized | ✅ before_record hooks remove secrets |
| No hardcoded credentials | ✅ Checked by linting rules |
| Secrets not in logs | ✅ Structlog filtering configured |
import os
# CORRECT: Load from environment
api_key = os.environ.get("BIOETL_UNIPROT_API_KEY")
# INCORRECT: Hardcoded value
api_key = "sk-EXAMPLE_DO_NOT_USE" # NEVER do this┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Bronze │ ──► │ Silver │ ──► │ Gold │
│ (Raw API) │ │ (Cleaned) │ │ (Validated) │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
▼ ▼ ▼
Schema checks PyArrow schema Pandera rules
at adapters enforcement + business logic
| Layer | Tool | Validation Type |
|---|---|---|
| Adapters | Custom schemas | Input format, required fields |
| Silver | PyArrow/Delta | Schema enforcement, type coercion |
| Gold | Pandera | Business rules, cross-field validation |
- Records failing validation are sent to Quarantine
- Quarantine records include: original data, error details, timestamp
- Thresholds: >5% errors = warning, >20% errors = batch failure
# Run dependency audit
pip-audit
# Check for known vulnerabilities
make security-check- Dependabot version updates:
.github/dependabot.yml(pip + github-actions + docker; weekly, grouped minor/patch for pip, grouped per ecosystem otherwise) - Dependabot alerts + security updates: must be enabled in GitHub Settings → Code security and analysis → Dependency graph / Dependabot alerts / Dependabot security updates (separate from
dependabot.ymlversion-update PRs) - Dependabot triage: owner
@SatoryKono(CODEOWNERS), weekly; SLA Critical ≤ 24h, High ≤ 72h (see05-github-policy.md §5). PRs must pass all checks; auto-merge forbidden. - Dependency review: PR-time fail-closed scan of lockfile/manifest diffs (
.github/workflows/dependency-review.yml,fail-on-severity: high) - detect-secrets: Runs in CI (
.github/workflows/security.yml) to prevent credential leaks - GitHub secret scanning: enabled with push protection and partner-pattern validity checks. Non-provider patterns stay disabled (noisy; #10264).
- Gitleaks: Runs in CI (
.github/workflows/security.yml) with.gitleaks.toml(--redact; no secret values in job summaries/comments) - pip-audit: Runs in CI (
.github/workflows/security.yml) for dependency vulnerability scanning.PYSEC-2026-3721/CVE-2026-3219is remediated atpip==26.2.1; do not restore--ignore-vulnfor that advisory. Residual mermaid/Grafana GHSA remain timeboxed to 2026-11-30 (#9853/#9859). Not a Scorecard dismiss. - OSV-Scanner: Primary lockfile scanner in CI (
.github/workflows/security.yml) againstuv.lock; job fails on HIGH/CRITICAL (and unknown severity), not Medium/Low. Do not addosv-scanner.toml. Residual mermaid-cli/Grafana GHSA:05-github-policy.md§2.3.2, re-triage #9859. Scorecard Vulnerabilities (#1294) stays open. - Bandit: Python anti-pattern scan in
.github/workflows/security.yml - CodeQL: excluded from required checks by owner decision (#11929). The pull-request coordinator runs in GitHub Actions. The retained CodeQL workflow does not prove active scanning. Optional analysis remains a separate decision.
deployment.providerisgithub_actions. Rulesets13643213and15730586areenforcement: disabled. Storedci/circleci: pr-gate-completeis not active CircleCI protection. Targetpr-gate-completeis not the context stored on those rulesets. Catalogenforcement_verified_date2026-10-09is not proof of active merge protection. A successfulpr-gate-completedoes not by itself block merge. An offline catalog match does not confirm live server rulesets. Retained configuration: advanced setup only (.github/workflows/codeql.yml, Python,build-mode: none). GitHub default setup staysnot-configured; do not enable both and do not addc-cpp/build-mode: autobuild. Python SAST uploads to Code scanning. Alert triage: BioETL Team, weekly Monday with Scorecard. - OpenSSF Scorecard: Weekly non-blocking baseline (
.github/workflows/scorecard.yml) - Syft SBOM: SPDX artifact on GitHub Release (
release.yml) and GHCR push (docker.yml) - zizmor: High-confidence GitHub Actions YAML audit on workflow/action changes (
.github/workflows/zizmor.yml) - Trivy: Container image scanning in
.github/workflows/docker.yml(SHA-pinned) - Update policy: Critical ≤ 24h, High ≤ 72h, Medium next sprint (see
05-github-policy.md §5)
BioETL uses the canonical mixed dependency strategy:
- compatibility ranges live in
pyproject.toml - reproducibility is enforced through committed
uv.lock - exact
==pins are reserved for justified exceptions, not the default policy
- Use GitHub Security Advisories private reporting for this repository:
https://github.com/SatoryKono/BioactivityDataAcquisition/security/advisories/new - Initial acknowledgment target: 72 hours
- Report — Submit a private vulnerability report through GitHub Security Advisories
- Acknowledgment — We respond within 72 hours
- Investigation — We assess severity and impact
- Fix — Patch developed and tested
- Disclosure — Coordinated public disclosure after fix
- Description of the vulnerability
- Steps to reproduce
- Potential impact assessment
- Suggested fix (if any)
| In Scope | Out of Scope |
|---|---|
| BioETL codebase | Third-party API security |
| Configuration handling | Infrastructure not owned by us |
| Data validation logic | Social engineering attacks |
| Secret management | Physical security |
- No hardcoded secrets or credentials
- Input validation for external data
- Proper error handling (no stack traces in production)
- VCR cassettes sanitized before commit
- Dependencies from trusted sources only
# ❌ Hardcoded secrets
API_KEY = "sk-secret-key-here"
# ❌ Unsanitized input in queries
query = f"SELECT * FROM {user_input}"
# ❌ Secrets in logs
logger.info(f"Using API key: {api_key}")
# ❌ Disabled SSL verification
httpx.get(url, verify=False)# ✅ Environment variables
api_key = os.environ["BIOETL_UNIPROT_API_KEY"]
# ✅ Parameterized queries
query = "SELECT * FROM compounds WHERE id = ?"
# ✅ Masked logging
logger.info("API key configured", has_key=bool(api_key))
# ✅ SSL verification enabled (default)
httpx.get(url) # verify=True by defaultLast updated: 2026-09-10