Repository navigation
feat: Quick PRD generator with SSRF-safe references and output validation - #213
Closed
AdeRistanto wants to merge 1 commit into
Closed
AdeRistanto wants to merge 1 commit into
AdeRistanto wants to merge 1 commit into
Conversation
…lidation - Quick PRD mode: single streaming LLM call that fills the "PRD Full Stack untuk AI Coding (Starter Next JS)" template (header/skeleton/footer) or a general PRD, served at POST /api/prd/stream and a new web UI (static/index.html). - Template updated to the latest framework: architecture diagram, sequence diagrams for critical flows, give-back markers, provider deadline/cancel rules, follow-up flags, owner columns, configurable limits, 29-item checklist and six cross-checks; system prompt aligned. - Reference website fetching hardened against SSRF: http/https on ports 80/443 only, no userinfo or internal host names, every DNS answer must be public (explicit IPv4/IPv6 deny lists incl. cloud metadata, NAT64/mapped unwrapping), connection pinned to the validated IP with Host header + TLS SNI/verification, per-hop redirect validation (max 3), trust_env=False, text content types only, 1 MB body cap, 15 s total timeout per URL, 4 concurrent fetches globally, generic client-facing errors, PRD_REFERENCE_FETCH=0 kill switch. - Post-generation validation: finish_reason/max_tokens reporting with truncation detection, leftover [placeholder] scan, required headings, balanced code fences, declared sequenceDiagram participants, and no token in the repo version. Results are shown per check in the UI; truncated PRDs are not marked done. - settings.toml: llm.max_tokens = 65536; pyproject/uv.lock: explicit httpx. - Tests: tests/test_prd_generator.py, test_prd_reference_security.py (offline, mocked DNS/transport), test_prd_validation.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Author
|
Opened by mistake (intended for a personal repository). Closing, sorry for the noise. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request
Description
Adds a Quick PRD mode: a complete Product Requirements Document is generated in one streaming LLM call and shown in a new web UI. Two templates are available:
fullstack_nextjs(the default, "PRD Full Stack untuk AI Coding (Starter Next JS)", written in Indonesian) andgeneral. This PR also hardens reference-website fetching against SSRF and validates every generated PRD automatically.Type of Change
Changes Made
Quick PRD generator (
gtplanner/agent/prd_generator.py,POST /api/prd/streaminfastapi_main.py,static/index.html)status,reference,chunk,validation,done.NGODINGPAKEAI_TOKENtaken from.env) and the footer. The LLM fillsprd_templates/fullstack_nextjs/skeleton.md.SSRF hardening of reference fetching (
fetch_reference). URLs found in the request are fetched with these rules:localhost,*.internal,*.local, single-label names).is_globalalone.Hostheader andsni_hostname. TLS verification stays on.trust_env=False, so proxy environment variables are ignored.text/html,application/xhtml+xmlandtext/plain;Content-Lengthover 1 MB is rejected; the streamed body is capped at 1 MB.PRD_REFERENCE_FETCH=0disables fetching completely, with no network access.Post-generation validation (
validate_prd, shown per item in the UI)finish_reasonandmax_tokensare reported.length/max_tokensmarks the PRD as truncated, and a truncated PRD is never shown as done.[placeholder]outside codengpk_orNGODINGPAKEAI_TOKENvalueConfig
settings.toml:llm.max_tokens = 65536. Without it the provider's default cut long PRDs off.Whole files were committed, so this PR also carries some earlier local edits to the same files that are not part of the feature:
pyproject.toml/uv.lock: addstruststore>=0.10. The code that uses it (gtplanner/__init__.py) is not in this PR, so the dependency is currently unused.settings.toml: Chinese comments translated to English;timeout = 600andmax_retries = 1forllm.fastapi_main.py: everything changed in this file, not only the/api/prd/streamroute and servingstatic/index.htmlat/.I can split these into a separate PR if you prefer.
Testing Performed
tests/test_prd_reference_security.py(135 tests) runs fully offline: DNS is faked, HTTP goes throughhttpx.MockTransport, and httpcore's realconnect_tcpis replaced by a recorder, so no socket is ever opened. The tests show that the real connection layer connects to the pinned IP and thatstart_tlsreceives the original host name withCERT_REQUIREDandcheck_hostname=True. Mutation check: disabling each control in memory makes the suite fail (IP checks 69, pinning 18, kill switch 6, error sanitising 68 failures; without the size cap a test never finishes).tests/test_prd_validation.py(43 tests) covers a passing and a failing case for each check, plus finish_reason handling. Each check was also disabled in memory to confirm the tests fail./api/prd/stream(e-commerce PRD with Midtrans and shipping):glm-5.3-flash:finish_reason=lengthafter about 22 min. The PRD was correctly flagged as truncated, probably because reasoning tokens count againstmax_tokens.deepseek-v4-flash:finish_reason=stopafter about 5.5 min, about 121k characters, all 5 checks passed.Not verified: real-network behaviour of the SSRF hardening (an actual TLS handshake to a pinned IP, OS/container DNS and IPv6 behaviour). Please test from a staging environment before relying on it in production.
Environment Tested
uv), httpx 0.28.1, httpcore 1.0.9glm-5.3-flash,deepseek-v4-flash)Breaking Changes
The existing agent planner endpoints are unchanged. Note that
llm.max_tokensnow applies to every LLM call.Documentation
Dependencies
No new dependencies
New dependencies added (list below)
httpx>=0.28.1: declared explicitly. It was already installed throughopenai. The locked version is unchanged (no other package versions changed inuv.lock).truststore>=0.10: see "Unrelated changes" above.Performance Impact
A full-stack PRD takes several minutes to generate and uses a large system prompt (about 12k characters plus the skeleton). Reference fetches add at most 15 s and now run in parallel.
Security Considerations
Out of scope and still open:
/api/prd/streamhas no authentication.*.0.0.0.0.URL_to_Markdown.py,search.pyvia Jina) are unchanged.NGODINGPAKEAI_TOKENvalue. The UI's "Download untuk repo" strips it, and the validation checks that.🤖 Generated with Claude Code