Record security audit findings
This commit is contained in:
205
docs/roadmap/audit/07-security.md
Normal file
205
docs/roadmap/audit/07-security.md
Normal file
@@ -0,0 +1,205 @@
|
||||
# Security Audit
|
||||
|
||||
Reviewed 2026-11-04 against commit `cf0bfd4` and the current worktree. Pending
|
||||
relocations of earlier audit documents do not change the production baseline
|
||||
and were left untouched.
|
||||
|
||||
## Scope, Architecture, And Threat Boundary
|
||||
|
||||
PromptKit is an in-process Go library with a public facade and internal
|
||||
configuration, execution, transport, validation, and observation components.
|
||||
ConfigKit owns source loading/composition; PromptKit interprets its final
|
||||
handoff and captures immutable definitions. Execution reads caller-selected
|
||||
artifacts, renders captured templates, resolves credentials, and contacts the
|
||||
selected backend. Prepared handles privately retain one execution's settings.
|
||||
Inspection and observation expose deliberately selected metadata.
|
||||
|
||||
Reviewed configuration and request validation, filesystem/resource selection,
|
||||
template and JSON handling, credential flow, public formatting and reports,
|
||||
HTTP requests/redirect handling, provider responses, schema loading, repair,
|
||||
backend capacity, prepared ownership, and release-entrypoint wiring. The review
|
||||
included interactions with the selected ConfigKit and JSON Schema dependencies
|
||||
and an advisory scan of runtime packages.
|
||||
|
||||
The [architecture policy](../../policy/architecture.md#repository-and-consumer-boundary)
|
||||
and [consumer security guidance](../../consumers/pkg-promptkit.md#protect-files-and-generated-data)
|
||||
place application authorization, untrusted path/endpoint admission, payload-size
|
||||
policy, and output disclosure with the consumer. Configuration sources,
|
||||
templates, and injected Go collaborators are application-selected capabilities.
|
||||
Upstream HTTP response bytes remain an external trust boundary. This review
|
||||
does not treat ordinary file selection, runtime endpoint overrides, namespace
|
||||
selection, or supported message roles as authorization mechanisms.
|
||||
|
||||
No production code or repository tests were changed, no secrets were collected,
|
||||
and no external provider or forge was probed. Network activity was limited to
|
||||
obtaining the public Go vulnerability checker/database and reading official
|
||||
advisories. This is a source review with focused diagnostics, not a claim that
|
||||
every possible consumer deployment is secure.
|
||||
|
||||
## Evidence
|
||||
|
||||
- Source/call-graph review and the applicable public GoDoc, framework formats,
|
||||
integration contract, policies, and ADR 0001.
|
||||
- Existing focused tests passed for credential/report formatting, sensitive
|
||||
configuration inspection/diffs, schema reference containment, provider-body
|
||||
bounds, JSON value cycles/depth, prepared ownership, and capacity boundaries.
|
||||
- A temporary offline public-API diagnostic used a controlled HTTP transport
|
||||
and a synthetic credential. It verified the authentication-body behavior in
|
||||
finding 2 and confirmed that credential was absent from the serialized report.
|
||||
- `govulncheck` v1.9.0 scanned `./...` using Go 1.27.0 and the Go vulnerability
|
||||
database last modified `2026-11-02T22:01:43Z`. The runtime scan covered the
|
||||
module, its examples, ConfigKit v1.1.0, jsonschema/v6 v6.0.2, YAML v4.0.0-rc.6,
|
||||
and x/text v0.14.0. It is not a complete audit of ReleaseKit's implementation
|
||||
or every dependency used only by the release tool.
|
||||
|
||||
The text-mode scan reported ten symbol-level standard-library advisories, plus
|
||||
package/module-only matches. Those are candidate matches, not ten established
|
||||
PromptKit vulnerabilities. Applicability was checked against the actual
|
||||
operations described below rather than inferred from interface-call traces.
|
||||
|
||||
## 1. The Audited Go Toolchain Predates Applicable Networking Security Fixes
|
||||
|
||||
**Priority:** P2. Build/deployment security finding, not a newly discovered
|
||||
PromptKit algorithm defect.
|
||||
|
||||
**Relevant code:** [declared Go minimum](../../../go.mod:3),
|
||||
[default HTTP-client construction](../../../internal/llm/openai_compatible_client.go:58),
|
||||
[generation dispatch](../../../internal/llm/openai_compatible_client.go:211), and
|
||||
[diagnostic dispatch](../../../internal/llm/http_evidence.go:129).
|
||||
|
||||
**Condition and impact:** The local toolchain used for this audit is Go 1.27.0.
|
||||
Applications built with that patch can exercise affected standard-library
|
||||
networking through PromptKit's normal outbound client:
|
||||
|
||||
- [GO-2026-6611](https://pkg.go.dev/vuln/GO-2026-6611) describes excessive CPU
|
||||
consumption caused by a malicious HTTP/2 peer repeatedly changing initial
|
||||
stream windows. An application using HTTP/2 against a hostile or compromised
|
||||
backend, with enough active streams for amplification, can suffer availability
|
||||
loss. The Go 1.27 fix is in 1.27.2. Body-size limits do not address protocol
|
||||
processing below response decoding.
|
||||
- [GO-2026-6809](https://pkg.go.dev/vuln/GO-2026-6809) adds explicit rejection of
|
||||
compromised intermediate certificates. The affected conditions depend on the
|
||||
platform and trust-store state. When the old trust path remains accepted, an
|
||||
attacker able to present a matching certificate chain and intercept the
|
||||
connection could compromise confidentiality/integrity of prompts, responses,
|
||||
or bearer credentials. The Go 1.27 fix is in 1.27.3. This is not a claim that
|
||||
every default system trust store remains affected.
|
||||
|
||||
**Evidence and limits:** The advisories establish affected versions and attack
|
||||
conditions; source review establishes use of the standard outbound HTTP/TLS
|
||||
path. No certificate forgery, HTTP/2 attack, or running consumer binary was
|
||||
tested. The scanner's surprising interface-based paths from unrelated methods
|
||||
are not relied upon as proof of exploitation. PromptKit is source-only: a
|
||||
consumer's actual compiled toolchain and transport configuration determine its
|
||||
exposure. The `go 1.27.0` directive is a compatibility minimum, not a record of
|
||||
which patch built an application.
|
||||
|
||||
**Potential remediation:** Use an appropriate current patched Go toolchain,
|
||||
at least 1.27.3 within this series for these two fixes, for release validation
|
||||
and ReleaseKit execution; advise consumers to rebuild affected applications
|
||||
with patched Go. Recheck advisories against that selected toolchain. This fits
|
||||
the existing distinction between consumer minimum and maintainer toolchain in
|
||||
the [release procedure](../../release.md#prepare-the-candidate).
|
||||
Whether to raise the consumer minimum for the identified fixes is a separate
|
||||
compatibility decision under the existing policy, not an automatic consequence
|
||||
of this audit. These fixes do not require custom TLS verification, a blanket
|
||||
HTTP/2 ban, or a new recurring release gate.
|
||||
|
||||
## 2. Authentication Probes Permit Unbounded Remote-Body Memory Consumption
|
||||
|
||||
**Priority:** P2. Existing availability finding, confirmed at the external
|
||||
response boundary. This is the same mechanism as resource-management audit
|
||||
finding 1, not a second independent remediation item.
|
||||
|
||||
**Relevant code:** [authentication recognition](../../../internal/usecase/smoke_authentication.go:139),
|
||||
[authentication probe execution](../../../internal/usecase/smoke_authentication.go:66),
|
||||
and [diagnostic HTTP execution](../../../internal/llm/http_evidence.go:129).
|
||||
|
||||
**Condition:** A consumer explicitly invokes an `openrouter_key` authentication
|
||||
diagnostic. The selected endpoint, a compromised provider, or an intermediary
|
||||
on an operator-selected plaintext connection returns HTTP 200 with a large or
|
||||
continuously growing body. Recognition uses unrestricted `io.ReadAll` followed
|
||||
by whole-document JSON unmarshalling. Unlike generation response decoding,
|
||||
this path has no byte budget. HTTP-only probes also do not use generation
|
||||
capacity admission.
|
||||
|
||||
**Impact:** The remote peer controls memory consumption before the diagnostic
|
||||
returns. A fast response can cause large allocations or process termination
|
||||
before a deadline, and concurrent probes multiply the exposure. A configured
|
||||
timeout bounds time, not bytes allocated during that interval. This requires a
|
||||
diagnostic call; it is not an unauthenticated inbound attack on a PromptKit
|
||||
server, and ordinary `Run` does not execute the probe.
|
||||
|
||||
**Evidence:** The offline diagnostic returned a minimal recognized account
|
||||
object and 4 MiB of irrelevant padding. PromptKit read all 4,194,374 bytes,
|
||||
closed the body, and concluded with credential acceptance. The body used an
|
||||
unknown content length. No deliberate memory exhaustion was attempted. The
|
||||
synthetic bearer credential was supplied to the controlled transport and was
|
||||
absent from the serialized diagnostic report.
|
||||
|
||||
**Potential remediation:** Select a finite body budget for this small diagnostic
|
||||
protocol and enforce it for declared and streamed lengths, closing on overflow
|
||||
without unbounded draining. Preserve factual reporting: overflow establishes
|
||||
neither credential rejection nor acceptance. Agree the limit and overflow
|
||||
classification explicitly; no existing authentication-body byte guarantee is
|
||||
being enforced. The existing resource-audit remediation can own this fix;
|
||||
there is no need for separate global limits on prompts or artifacts.
|
||||
|
||||
## Other Candidates And Boundary Checks
|
||||
|
||||
- **Scanner applicability:** Server-only advisories GO-2026-6603, 6612, 6613,
|
||||
and 6617 do not establish an attack on PromptKit's outbound client. ECH
|
||||
server parsing (6607), multipart-form parsing (6608), and Range-serving
|
||||
behavior (6609) likewise lack a matching operation here. The CONNECT-with-body
|
||||
condition in 6605 and raw HTTP/2-to-HTTP/1 header forwarding in 6610 are absent
|
||||
from built-in POST/GET generation and factual diagnostic reporting. Windows
|
||||
junction-directory creation (6604) is not established by read-only resource
|
||||
opens. These exclusions concern PromptKit, not all possible code in a host
|
||||
application.
|
||||
- **Module-only matches:** x/text advisories GO-2026-5970 and 6629 were not
|
||||
reported as called vulnerable symbols; no matching use of `norm.Iter` or
|
||||
`precis.Nickname` was established. html/template advisories 6599 and 6600 do
|
||||
not apply to the text/template renderer. No separate runtime third-party
|
||||
vulnerability was substantiated by this scan; that is not a general safety
|
||||
certification of those modules.
|
||||
- **Files and schemas:** Ordinary artifacts intentionally allow unrestricted
|
||||
caller-selected regular files. Host-backed `content_file` contexts use
|
||||
ConfigKit's rooted filesystem, while the standard schema loader checks
|
||||
canonical containment and rejects remote reference schemes. Generic `fs.FS`
|
||||
inputs remain caller-supplied capabilities; logical path checks do not turn an
|
||||
arbitrary filesystem implementation into a host sandbox. No additional
|
||||
confinement guarantee is proposed.
|
||||
- **Injection:** Variables and artifact text are rendered as data rather than
|
||||
reparsed as Go templates. Captured templates have the input helper and normal
|
||||
text/template functionality, not a shell or process-execution helper.
|
||||
Provider payloads use JSON encoding and reserved-field checks. Model output
|
||||
is data for validation/repair, not executable code or an authorization
|
||||
decision. Prompt injection and domain-aware interpretation remain consumer
|
||||
concerns.
|
||||
- **Credentials and disclosure:** Direct keys are request-scoped, excluded from
|
||||
supported JSON and public request summaries, and removed from diagnostic
|
||||
settings and prepared details. Provider error defaults redact details;
|
||||
explicit provider accessors and arbitrary application content retain the
|
||||
documented consumer disclosure responsibility. Inspection/diff tests protect
|
||||
configured sensitive provider paths. No blanket redaction of arbitrary
|
||||
prompt, schema, variable, error, or provider content is promised.
|
||||
- **Ownership and availability:** Engine snapshots and copied public values
|
||||
prevent ordinary cross-call mutation. Prepared handles enforce owner binding
|
||||
and single claim. Capacity and retry cleanup are tested. Intentionally
|
||||
unlimited backends, synchronous template/schema dependency calls, and
|
||||
application-selected large files are not newly classified as vulnerabilities.
|
||||
The other resource-audit findings remain maintenance items rather than
|
||||
independently demonstrated remote attacks.
|
||||
- **Release tooling:** Local launchers pass quoted arguments through `exec`;
|
||||
there is no PromptKit runtime command-execution surface. The delivery token
|
||||
path is ignored and selected for committed-file protection. Publication,
|
||||
token-file permission enforcement, and recovery implementation belong to
|
||||
ReleaseKit; no publication command was run during this audit.
|
||||
|
||||
## Recommended Follow-Up
|
||||
|
||||
Use a patched Go toolchain before release preparation and address the existing
|
||||
authentication-body availability finding. Neither requires a new application
|
||||
authorization subsystem or stronger filesystem, endpoint, or semantic-output
|
||||
contracts. No additional PromptKit-specific confidentiality, integrity, or
|
||||
authorization defect was substantiated within the documented boundary.
|
||||
Reference in New Issue
Block a user