Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/evidence/CHECKLIST_DEPENDENCY_UPDATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Dependency Update Checklist

Steps for updating a dependency.

- [ ] Check the new version's changelog for breaking changes.
- [ ] Update the version in pyproject.toml.
- [ ] Run `pip install -e ".[dev]"` to install the update.
- [ ] Run `python3 -m pytest -q` to verify nothing is broken.
- [ ] If tests fail, fix compatibility issues.
- [ ] Update any documentation that references the old version.
- [ ] If the dependency has a security fix, note it in the PR.

## Frequency
- Security updates: apply within 1 week.
- Minor updates: apply within 1 month.
- Major updates: evaluate on a case-by-case basis.
16 changes: 16 additions & 0 deletions docs/evidence/GUIDE_PRIVACY_THREAT_MODEL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Artifact Privacy Threat Model

Potential privacy threats related to pipeline artifacts.

| Threat | Risk | Mitigation |
|--------|:----:|------------|
| Candidate sequences in logs | Low | Logs are local-only |
| Lab results contain PII | Medium | Lab partner agreement, data return schema excludes PII |
| Evidence certificates shared without context | Low | All certs include disclaimer |
| Manifest reveals internal paths | Low | Paths are relative, no user info |

## Principles
- Pipeline outputs should not contain PII.
- If PII is present, it must be clearly labeled and handled per data governance policy.
- All artifacts include a disclaimer stating they are computational predictions.
- Artifacts shared externally should be reviewed for sensitive content.
23 changes: 23 additions & 0 deletions docs/evidence/GUIDE_STRUCTURED_LOGGING_OPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Structured Logging Option

How to enable structured logging.

## Current State
Pipeline uses print statements and subprocess output. There is no
structured logging framework.

## Recommendation
Add Python's `logging` module with structured formatting:
```python
import logging
logging.basicConfig(
format="%(asctime)s %(levelname)s %(name)s:%(lineno)d %(message)s",
level=logging.INFO,
)
```

## Benefits
- Consistent log format across all modules.
- Log levels (DEBUG, INFO, WARNING, ERROR) enable filtering.
- Structured format enables log parsing and analysis.
- No external dependencies required.
21 changes: 21 additions & 0 deletions docs/evidence/POLICY_DEPENDENCY_RISK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Dependency Risk Review Policy

How to review dependency risks.

## Risk Levels
| Level | Criteria | Review Required |
|:-----:|----------|:---------------:|
| Low | Well-known library, stable API, permissive license | None |
| Medium | Less common library, breaking changes possible | Maintainer |
| High | New dependency, restrictive license, security-sensitive | Project lead |

## Required Checks
- License compatibility with Apache 2.0
- Known security vulnerabilities
- Maintenance status (recent releases, issue response)
- Number of transitive dependencies

## Prohibited
- Dependencies with GPL or AGPL licenses (unless absolutely necessary and reviewed)
- Dependencies with known critical vulnerabilities
- Unmaintained dependencies (no release in > 2 years)
20 changes: 20 additions & 0 deletions docs/evidence/POLICY_HASH_ALGORITHM.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Hash Algorithm Policy

Hash algorithms used in the pipeline.

## Current Algorithms
| Algorithm | Used For | Strength |
|-----------|----------|:--------:|
| SHA-256 | Evidence certificates, manifests, chain of custody | Strong |
| SHA-256 | Input file hashing for reproducibility | Strong |

## Requirements
- All hashes must use SHA-256 or stronger.
- MD5 and SHA-1 are forbidden for security-sensitive uses.
- Hash algorithms must be documented in the relevant schema.
- Hash values must be hex-encoded, lowercase.

## Verification
- Hash verification is built into chain-of-custody scripts.
- Run `scripts/verify_lab_peptides.py` to verify sequence hashes.
- Manifests include hashes for all input files.
16 changes: 16 additions & 0 deletions docs/evidence/POLICY_SAFE_ARCHIVE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Safe Archive Extraction Policy

How to safely extract archives in the pipeline.

## Rules
- Only extract archives from trusted sources.
- Validate archive contents before extraction.
- Prevent path traversal attacks (no `../` in extracted paths).
- Set a maximum file size and count.
- Extract to a dedicated directory, not to an existing directory tree.
- Verify extracted file hashes against expected values when available.

## Implementation
- The `build_lab_batch_pack.py` script creates ZIP archives.
- Extraction should use `zipfile.ZipFile.extractall()` with path validation.
- Path traversal is prevented by checking that all extracted paths are within the target directory.
25 changes: 25 additions & 0 deletions docs/evidence/PROPOSAL_CLI_COMPLETION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# CLI Completion Support Proposal

Adding shell completion support for the CLI.

## Motivation
Shell completion improves developer experience by showing available
commands, flags, and values.

## Approach
Use Python's `argparse` with `argcomplete` for shell completion.

## Required Changes
1. Add `argcomplete` as an optional dependency.
2. Add shell completion entry point in `pyproject.toml`.
3. Register argparse parsers with `argcomplete.autocomplete()`.
4. Document how to enable completion in the shell.

## Completion Types
- Command names: `rank`, `validate`, `bench`, etc.
- Flag names: `--candidates`, `--out`, `--config`, etc.
- File paths: for `--candidates`, `--out` flags.
- Enum values: for `--ranking-mode` (ensemble, expert).

## Status
Proposed — not yet implemented.
23 changes: 23 additions & 0 deletions tests/test_manifest_completeness.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""Tests for archive manifest completeness."""
import json
from pathlib import Path


def test_run_manifest_has_required_keys():
"""Verify run_manifest.schema.json has required fields."""
schema = json.loads(Path("schemas/run_manifest.schema.json").read_text())
required = {"run_id", "pipeline_version", "config_hash", "generated_at", "inputs", "outputs"}
if "required" in schema:
assert required.issubset(set(schema["required"])), "Missing required fields in schema"


def test_candidate_schema_has_required_keys():
schema = json.loads(Path("schemas/candidate.schema.json").read_text())
assert "candidate_id" in str(schema)
assert "scores" in str(schema)


def test_lab_result_schema_has_required_keys():
schema = json.loads(Path("schemas/lab_result.schema.json").read_text())
for field in ["candidate_id", "assay_type", "result_value", "result_unit"]:
assert field in str(schema), f"Missing {field} from lab_result schema"
20 changes: 20 additions & 0 deletions tests/test_path_traversal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""Tests for path traversal in archive builders."""
import zipfile
import tempfile
from pathlib import Path


def test_zip_path_traversal_detected():
"""Verify that path traversal via ../ in zip entries can be detected."""
with tempfile.TemporaryDirectory() as tmp:
malicious_zip = Path(tmp) / "malicious.zip"
with zipfile.ZipFile(malicious_zip, "w") as zf:
zf.writestr("../../etc/passwd", "malicious")
detected = False
with zipfile.ZipFile(malicious_zip, "r") as zf:
for name in zf.namelist():
resolved = (Path(tmp) / name).resolve()
if not str(resolved).startswith(str(Path(tmp).resolve())):
detected = True
assert detected, "Path traversal was not detected"
assert "../../" in name, "Path traversal pattern should contain ../"
17 changes: 17 additions & 0 deletions tests/test_policies_batch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""Tests for policies and infrastructure batch."""
from pathlib import Path

FILES = [
"CHECKLIST_DEPENDENCY_UPDATE.md",
"GUIDE_PRIVACY_THREAT_MODEL.md",
"POLICY_DEPENDENCY_RISK.md",
"POLICY_HASH_ALGORITHM.md",
"POLICY_SAFE_ARCHIVE.md",
"GUIDE_STRUCTURED_LOGGING_OPTION.md",
"PROPOSAL_CLI_COMPLETION.md",
]


def test_all_exist():
for f in FILES:
assert Path("docs/evidence", f).exists(), f"Missing: {f}"
48 changes: 48 additions & 0 deletions tests/test_stdout_stderr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""Tests that CLI commands properly separate stdout and stderr."""
import subprocess
import sys

import pytest


def _run_cmd(*args: str) -> subprocess.CompletedProcess:
return subprocess.run(
[sys.executable, "-m", "openamp_foundry.cli", *args],
capture_output=True, text=True,
env={"PYTHONPATH": "src"},
)


def test_rank_output_goes_to_stdout():
r = _run_cmd("rank", "--help")
assert r.returncode == 0
assert len(r.stdout) > 0


def test_validate_output_goes_to_stdout():
r = _run_cmd("validate", "--help")
assert r.returncode == 0
assert len(r.stdout) > 0


def test_bench_help_goes_to_stdout():
r = _run_cmd("bench", "--help")
assert r.returncode == 0
assert len(r.stdout) > 0


def test_missing_file_error_goes_to_stderr():
r = _run_cmd("rank", "--candidates", "/nonexistent.csv", "--out", "/tmp/x.jsonl")
assert r.returncode != 0
assert len(r.stderr) > 0 or "Error" in r.stdout


@pytest.mark.parametrize("cmd", [
["rank", "--help"],
["validate", "--help"],
["bench", "--help"],
])
def test_help_uses_stdout(cmd):
r = _run_cmd(*cmd)
assert r.returncode == 0
assert len(r.stdout) > 0
Loading