Skip to content

feat(policy): validate prompt policy bundles - #467

Draft
tumberger wants to merge 1 commit into
jens/eng-624-c1-prompt-hook-wirefrom
jens/eng-624-c2-signed-policy-bundle
Draft

tumberger wants to merge 1 commit into
jens/eng-624-c1-prompt-hook-wirefrom
jens/eng-624-c2-signed-policy-bundle

Conversation

@tumberger

@tumberger tumberger commented Aug 14, 2026 •

Copy link
Copy Markdown
Contributor

ENG-624 CLI stack 2/6; stacked on #466.

Adds the strict V2 synchronous prompt-policy client, portable deployment-identity calculation, source-hash checks, audience/parent/TTL validation, bounded response sizes, and typed HTTP failures.

Bundles are validated as live HTTPS responses and activated only after their complete authority-bearing identity is recomputed. No signing keys or policy-signature configuration are required.

tumberger commented Aug 14, 2026 •

Copy link
Copy Markdown
Contributor Author

@greptile-apps

greptile-apps Bot commented Aug 15, 2026

Copy link
Copy Markdown

Greptile Summary

The PR introduces a strict synchronous prompt-policy HTTP client and signed-bundle contract with source hashing, deployment identity, audience and validity checks, Ed25519 verification, and typed API errors.

  • Adds request and response contracts with strict JSON decoding.
  • Adds bundle identity, source-hash, signature, audience, parent, and TTL verification.
  • Adds public-key parsing and client/verifier tests.

Confidence Score: 4/5

The response-size bug should be fixed before merging because the client can reject policy bundles that satisfy its own decoded-source contract; transport validation is also worth tightening before production wiring.

The client limits encoded JSON to roughly the same size as the allowed decoded source even though JSON escaping can expand valid content substantially, causing silent truncation followed by decoding failure.

Files Needing Attention: internal/promptpolicy/client.go

Security Review

No currently reachable production security vulnerability was identified. The new client should nevertheless enforce HTTPS or loopback HTTP itself before it is wired into production, because it handles bearer credentials and prompt contents.

Important Files Changed

Filename Overview
internal/promptpolicy/client.go Adds the synchronous authenticated client, but its encoded-response bound can truncate valid bundles and URL validation does not enforce the established secure-transport boundary.
internal/promptpolicy/contract.go Defines strict V2 bundle decoding, validation, hashing, and identity calculation; its decoded source limit exposes the client's undersized wire bound.
internal/promptpolicy/verifier.go Adds cloned trust-key storage, audience and validity checks, Ed25519 verification, and strict public-key parsing without an independently established defect.
internal/promptpolicy/client_test.go Covers the request route and typed HTTP errors but does not exercise encoded responses that expand beyond the decoded policy limit.
internal/promptpolicy/contract_test.go Covers valid signatures, source tampering, audience mismatch, strict JSON decoding, and trust-key parsing.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant Client as Prompt-policy client
    participant API as Policy API
    participant Verifier
    Caller->>Client: Put(token, prompt, audience, parent)
    Client->>API: PUT prompt-policy resource
    API-->>Client: Signed policy bundle
    Client->>Client: Strict decode and structural validation
    Client-->>Caller: Bundle
    Caller->>Verifier: Verify(bundle, expected audience)
    Verifier->>Verifier: Recompute source hash and identity
    Verifier->>Verifier: Check audience, parent, validity window
    Verifier->>Verifier: Verify Ed25519 signature
    Verifier-->>Caller: Accepted or verification error
Loading

Reviews (1): Last reviewed commit: "feat(policy): verify signed prompt polic..." | Re-trigger Greptile

return Bundle{}, fmt.Errorf("send prompt-policy request: %w", err)
}
defer response.Body.Close()
data, err := io.ReadAll(io.LimitReader(response.Body, maxPolicySetBytes+64*1024))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Encoded response limit truncates bundles

When a contract-valid policy near the 1 MiB decoded limit contains JSON-escaped characters, this reader truncates the encoded response before DecodeBundle sees it, causing a valid bundle to fail JSON decoding.

Suggested change
data, err := io.ReadAll(io.LimitReader(response.Body, maxPolicySetBytes+64*1024))
data, err := io.ReadAll(io.LimitReader(response.Body, 6*maxPolicySetBytes+64*1024))

Comment on lines +42 to +45
if err != nil || parsed.Scheme == "" || parsed.Host == "" {
return nil, errors.New("invalid prompt-policy base URL")
}
if httpClient == nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 security Remote plaintext transport remains allowed

NewClient accepts non-loopback HTTP and arbitrary schemes even though Put sends bearer credentials and prompt contents through the resulting URL. Enforcing the repository's HTTPS-or-loopback-HTTP boundary here prevents future client wiring from exposing those values over plaintext and rejects unsupported schemes during configuration. How this was verified: The new URL check was compared with the HTTPS-or-loopback guards used by both sibling authenticated clients.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@tumberger
tumberger force-pushed the jens/eng-624-c2-signed-policy-bundle branch from a5516fa to 7390811 Compare August 15, 2026 13:31
@tumberger tumberger changed the title feat(policy): verify signed prompt policy bundles feat(policy): validate prompt policy bundles Aug 15, 2026
@tumberger
tumberger marked this pull request as draft August 15, 2026 13:34
@tumberger
tumberger force-pushed the jens/eng-624-c1-prompt-hook-wire branch from b0eb50f to 83d551c Compare August 26, 2026 09:00
@tumberger
tumberger force-pushed the jens/eng-624-c2-signed-policy-bundle branch from 7390811 to a3cfb57 Compare August 26, 2026 09:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant