Skip to content
Closed
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
37 changes: 37 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Architecture

Canonical product architecture for `contextual-orchestrator` lives in
[`docs/architecture.md`](docs/architecture.md).

## Role

Org LLM gateway: OpenAI-compatible front door with cost-aware routing, sync vs
batch policy, multi-agent conduct path (thinker → worker → verifier →
synthesizer), KV-backed credentials, and commercial evidence surfaces.

## Control flow

```text
Client → server.py (auth / validate)
→ TaskOrchestrator.complete
├─ route (single worker; optional price_per_million tie-break)
└─ conduct (workflow steps + access lists)
→ ModelClient (mock:// or HTTPS; retries; failover; circuit breaker)
→ OpenAI-shaped completion / SSE
```

## Security & compliance notes

- Runtime secrets: `get_credential` (KV), not request-time `os.getenv`.
- Provider egress: block loopback/private/reserved; TLS verify by default.
- Release authorization evidence is fail-closed on exact protected-head checks
(see `docs/commercial_release_candidate.md`).
- Vulnerability disclosure: `SECURITY.md` and
`docs/doctoring/security-disclosure-lifecycle.md` (ISO/IEC 29147 / 30111;
NIST SSDF).

## Research grounding

Paper-backed routing claims (Fugu, TRINITY, Conductor) are contracted in
`tests/test_paper_contracts.py` and summarized in `docs/architecture.md` with
APA 7th citations under `docs/papers/` when redistribution permits.
58 changes: 53 additions & 5 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,66 @@
# Security Policy

This policy defines the public vulnerability-reporting and coordinated-disclosure boundary for `ContextualWisdomLab/contextual-orchestrator`. It is informed by ISO/IEC 29147:2018 for vulnerability disclosure and ISO/IEC 30111:2019 for vulnerability handling. The evidence basis and review dates are recorded in `docs/doctoring/security-disclosure-lifecycle.md`.

## Supported Versions

No stable release currently exists, and `main` is not a supported release.
When the first stable release is published, this section will name its supported
version or release line.

Security fixes are prepared for the latest supported release and, when a vulnerability materially affects an older release that is still explicitly supported, for that supported line as well. Development branches, historical tags, archived artifacts, forks, and unreleased commits are not represented as supported production versions merely because they remain accessible.

When no stable release has been published, `main` is the integration reference but is not itself a release-support promise. A GitHub Security Advisory or release advisory is the authoritative place to identify affected and patched versions for a specific vulnerability.

## Scope

In scope are vulnerabilities in this repository's maintained source, packaging, release artifacts, provider-neutral orchestration interfaces, authentication and credential boundaries, network egress controls, persistence boundaries, and first-party GitHub Actions workflows.

Reports about third-party services or dependencies are useful when they demonstrate an impact on this project, but upstream-only defects should normally be reported to the responsible upstream maintainer. Findings in unrelated ContextualWisdomLab repositories should be reported through those repositories' own security channels. Do not use a vulnerability report as authorization to test third-party infrastructure, access data that is not yours, degrade service, or bypass provider terms.

## Reporting a Vulnerability

Report suspected vulnerabilities through GitHub private vulnerability reporting for `ContextualWisdomLab/contextual-orchestrator`:
Use GitHub private vulnerability reporting for `ContextualWisdomLab/contextual-orchestrator` whenever it is available:

https://github.com/ContextualWisdomLab/contextual-orchestrator/security/advisories/new

A useful report includes the affected component and version or commit, prerequisites, reproducible steps, observed impact, expected impact boundary, and any safe proof-of-concept material needed to validate the issue. Remove credentials, personal data, private model reasoning, and unrelated customer or provider data.

If private reporting is unavailable, open a public issue that contains only a request for a secure disclosure channel. Do not include exploit details, secrets, personal data, or unreleased vulnerability details in a public issue.

## Response Process
Before any stable release, maintainers must verify that private vulnerability
reporting is enabled and that security-notification recipients are configured.
If that private channel cannot be maintained, release authorization remains
blocked until this policy names a monitored alternative private contact.

## Coordinated Disclosure Lifecycle

1. **Receive and acknowledge.** Maintainers triage a private report and aim to acknowledge a credible report within 5 business days. This acknowledgement target is a communication objective, not a remediation SLA and not a promise that validation or a fix will complete within five days.
2. **Validate and scope.** Maintainers reproduce the report where practical, classify affected versions and deployment assumptions, identify downstream or multi-vendor coordination needs, and keep unpatched technical details private.
3. **Remediate and verify.** A fix is developed through a private security collaboration or another access-controlled path when premature disclosure would increase risk. Security-sensitive fixes must retain the repository's tests, coverage, security scanning, provenance, branch-protection, and independent-review requirements rather than bypass them.
4. **Coordinate release.** Maintainers and the reporter coordinate a disclosure point that reasonably allows a verified fix or mitigation to be available. Multi-vendor issues may require additional coordination time.
5. **Publish evidence.** When disclosure is appropriate, publish a GitHub Security Advisory and release or upgrade guidance that identifies affected versions, impact, remediation or mitigation, and patched versions. Request a CVE through the applicable advisory process when warranted and available.
6. **Learn and prevent recurrence.** Record the relevant root-cause class, regression evidence, and preventive control without publishing credentials, private data, or unnecessary exploit-enabling detail.

Reporter credit is offered when requested and appropriate, subject to the reporter's preference, coordinated-disclosure needs, and GitHub advisory capabilities. A reporter may also request not to be credited.

## Safe Harbor and Research Boundaries

We support good-faith security research that stays within the scope above, avoids privacy violations and service degradation, uses the minimum access needed to demonstrate the issue, stops when unintended sensitive data is encountered, and gives maintainers a reasonable opportunity to remediate before public disclosure. This policy does not authorize activity against third-party systems, physical systems, accounts or data you do not control, or conduct prohibited by applicable law or provider terms.

Do not intentionally persist, download, modify, or disclose data that is not yours. Do not perform denial-of-service testing, social engineering, credential stuffing, destructive testing, or high-volume automated probing against production services. If testing unexpectedly exposes sensitive information, stop, preserve only the minimum evidence needed to report the issue, and disclose it privately.

## Advisory and Release Evidence

A vulnerability is not considered remediated merely because a patch exists on a branch. The [canonical release guide](docs/RELEASE_GUIDE.md) governs the release-evidence states. Release evidence must identify the exact integrated revision and the released revision and must not treat queued, pending, skipped-required, cancelled, failed, absent, stale-head, predecessor-head, author-only, status-only, synthetic-merge-only, rate-limited, or infrastructure-only evidence as passing. Security advisories should identify the affected and patched version ranges and link to release or upgrade guidance when practical.

This policy does not replace repository merge policy: qualifying independent review, unresolved-finding disposition, required checks, branch protection, packaging, provenance, and release-acceptance controls remain authoritative for security releases.

- A maintainer should acknowledge a valid private report within 5 business days.
- Security fixes should be handled on a private branch until the patch is ready to publish.
- Public disclosure should include affected versions, impact, mitigation, and upgrade guidance.
The canonical [threat model](docs/THREAT_MODEL.md) defines assets, trust zones,
abuse cases, controls, and residual risk. The [incident
runbook](docs/INCIDENT_RUNBOOK.md) defines triage, containment, evidence
preservation, recovery, and post-incident acceptance. Neither document claims
an external certification or attestation.

## Automated Checks

Expand Down
6 changes: 3 additions & 3 deletions contextual_orchestrator/cost_ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ def _seed_dimension_catalog(self) -> None:
ph = self._placeholder()
cur = self._conn.cursor()
for order, (name, label, _column) in enumerate(ATTRIBUTION_DIMENSION_CATALOG):
cur.execute(
cur.execute( # nosemgrep -- sqlalchemy-execute-raw-query FP: only the DB-API placeholder char is interpolated; the value is bound.
f"SELECT 1 FROM cost_attribution_dimensions WHERE dimension_name = {ph}", # nosec B608 - ph is a DB-API placeholder.
(name,),
)
Expand All @@ -602,7 +602,7 @@ def append(self, record: UsageRecord) -> None:
placeholders = ", ".join(ph for _ in _USAGE_COLUMNS)
columns = ", ".join(_USAGE_COLUMNS)
cur = self._conn.cursor()
cur.execute(
cur.execute( # nosemgrep -- sqlalchemy-execute-raw-query FP: columns are the fixed _USAGE_COLUMNS constant; values are bound.
f"INSERT INTO llm_usage_records ({columns}) VALUES ({placeholders})", # nosec B608 - columns are fixed _USAGE_COLUMNS.
tuple(row.get(column) for column in _USAGE_COLUMNS),
)
Expand All @@ -622,7 +622,7 @@ def query(self, start: Optional[int] = None, end: Optional[int] = None) -> List[
where = f" WHERE {' AND '.join(clauses)}" if clauses else ""
columns = ", ".join(_USAGE_COLUMNS)
cur = self._conn.cursor()
cur.execute(f"SELECT {columns} FROM llm_usage_records{where}", tuple(params)) # nosec B608 - columns and clauses are fixed.
cur.execute(f"SELECT {columns} FROM llm_usage_records{where}", tuple(params)) # nosec B608 - columns and clauses are fixed. # nosemgrep -- sqlalchemy-execute-raw-query FP: fixed columns and clause templates; all values are bound.
return [dict(zip(_USAGE_COLUMNS, values)) for values in cur.fetchall()]


Expand Down
4 changes: 2 additions & 2 deletions contextual_orchestrator/orchestrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ def __init__(
@staticmethod
def _build_ssl_context(ca_bundle: str | None, verify_tls: bool) -> ssl.SSLContext:
if not verify_tls:
return ssl._create_unverified_context() # nosec B323 - explicit dev-only provider TLS opt-out.
return ssl._create_unverified_context() # nosec B323 - explicit dev-only provider TLS opt-out. # nosemgrep -- unverified-ssl-context: intentional, default-secure (verify_tls defaults True) dev-only opt-out for self-signed endpoints.
if ca_bundle:
if not os.path.isfile(ca_bundle):
raise ValueError(f"provider CA bundle does not exist: {ca_bundle}")
Expand Down Expand Up @@ -307,7 +307,7 @@ def _send(self, agent: ModelAgent, payload: dict[str, Any]) -> str:

def _open_provider(self, request: urllib.request.Request) -> Any:
"""Open a provider request built from a validated provider URL."""
return urllib.request.urlopen( # nosec B310 - request URL comes from _provider_url after provider validation.
return urllib.request.urlopen( # nosec B310 - request URL comes from _provider_url after provider validation. # nosemgrep -- dynamic-urllib-use: URL is built by _provider_url after scheme/host validation; egress to loopback/private/reserved is blocked.
request,
timeout=self.timeout,
context=self._ssl_context,
Expand Down
Loading
Loading