Repository navigation
feat(inflow-lab): public demo proxy so /tools/inflow-lab works signed-out - #3797
Conversation
Add /api/inflow-lab-demo with a strict method+path allowlist, per-IP rate limit, same-origin + CSRF on mutations, tenant-field stripping, body size cap, and fresh upstream headers (server Basic only — never forward cookies or caller tokens). Leave requireProxyAuth and the authed /api/inflow-lab proxy unchanged; point the tools client at the demo path so /tools/inflow-lab works signed-out. Signed-off-by: C.Lee Sellers <131207452+PetrefiedThunder@users.noreply.github.com> Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
PR Blocker ChecklistHead SHA: Status: Ready
|
Findings
|
✅ Bundle Size Report
Bundle is within the 10.0MB limit. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4c0e249da4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Vercel preview: https://regengine-5xjn7exeu-petrefiedthunders-projects.vercel.app Access is via Vercel login (Deployment Protection). |
PetrefiedThunder
left a comment
There was a problem hiding this comment.
Verdict: FAIL (security review)
Head reviewed: 4c0e249da487ee6efd6402e480529b8c539ad8ae (exact match; unchanged)
Authorship: clear — Christopher / PetrefiedThunder only (not codex). Review proceeded.
Blocking
-
Rate limit trivially bypassable via spoofed
X-Forwarded-For.clientIp()trusts the first XFF hop. An attacker can rotateX-Forwarded-For: <new-ip>per request and reset the token bucket. Prefer a platform-trusted client IP (Vercel connection IP / rightmost trusted hop), ignore client-supplied XFF for bucketing, or document + enforce that only the edge may set it. Tests currently drive rate-limit via client XFF, which encodes the unsafe trust model. -
Upstream JSON responses are not tenant-stripped. Request bodies run through
stripTenantLike, but the handler returnsNextResponse.json(payload)from upstream as-is. Tenant-like keys in demo upstream responses are reflected to anonymous callers. Strip (or allowlist) response JSON the same way as the request body before reflecting. -
Path allowlist IDs are not traversal-hardened.
pathFromParamsonly rejects exact..and NUL. Lineage / demo-fixture ID segments that contain../,..\\,%2e%2e, encoded slashes, or backslashes still satisfy[^/]+afterencodeURIComponentand can reach upstream as/api/lineage/..%2F…or/api/demo-fixtures/..%2F…/load. Reject any segment matching\.\.|%2e|\\|%5c|%2f(case-insensitive) before allowlisting; add tests for%2e%2e, double-encoding, backslashes, and encoded slashes. -
Copy gate failed on this head.
sample-democount rose infrontend/src/app/api/inflow-lab-demo/[...path]/route.ts(main=0 → actual=2; baseline stale). Error/hint strings use “demo” wording the copy gate bans. Required Copy gate is red. -
Client wholesale base switch breaks authenticated exports.
INFLOW_API→/api/inflow-lab-demoalso rewritescsvExportHref/epcisExportHrefviaservicePath()onto paths the demo proxy deliberatelyNOT_ALLOWLISTEDs. Preview export controls 404 even when the authed/api/inflow-labproxy (stillrequireProxyAuth) is enabled. Keep export hrefs on the authed proxy, or hide/disable them in the public demo. Open Codex thread on this.
Checklist (this head)
| Check | Result |
|---|---|
| Allowlist = healthz, simulate/status, start, stop, reset, events, lineage, demo-fixtures only | Pass (contract tight; exports correctly excluded) |
Path traversal blocked (.., %2e%2e, encoded slashes, double-encoding, \) |
Fail (see #3) |
| Method smuggling blocked; only expected methods per route | Pass enough (only GET/POST exported; contract is method+path; override headers ignored by App Router) |
| Per-IP rate limit not XFF-spoofable | Fail (see #1) |
| Same-origin + CSRF on mutations | Pass |
| No cookies / caller auth / tenant headers forwarded; basic-auth server-side only | Pass (fresh Headers, credentials: "omit", env Basic only) |
| Tenant data not reflected back | Fail on responses (see #2); query tenant_id stripped |
requireProxyAuth on /api/inflow-lab intact |
Pass (untouched) |
Flag off by default (REGENGINE_ENABLE_INFLOW_LAB_DEMO_PROXY) |
Pass in production; non-prod auto-on |
| No payment/auth/compliance changes | Pass (4 files: demo route, tests, client base, .env.example) |
| Copy rules | Fail (Copy gate red; see #4) |
CI note
Required gates otherwise largely green (Unit Tests, Secrets, Workflow Contract, SAST, Security Audit, Production Readiness, Frontend CI Status Gate). Copy gate fail blocks green. Engine Review waiting on non-author review. 1 open review thread (export base). No merge.
REVIEW: FAIL codex 4c0e249
Co-authored-by: PetrefiedThunder <131207452+PetrefiedThunder@users.noreply.github.com>
Use platform-trusted client IP for rate limiting, strip tenant-like fields from proxied JSON responses, harden path-ID validation against encoded traversal, allowlist export preview GETs under the public base with safeHref, and clear remaining sample/demo copy in the tools page. Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com>
Codex FAIL follow-up (
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 986f32a. Configure here.
PetrefiedThunder
left a comment
There was a problem hiding this comment.
Verdict: FAIL (delta re-review)
Head reviewed: 986f32a81c0334e216ac500474e03fe6457abfca (exact match; merge-tree equals head tree; linear parent ee1fccba).
Authorship: Christopher / PetrefiedThunder (+ one Copilot copy-gate fix commit). Not codex-authored. Review proceeded.
Prior FAIL items (5750061305 @ 4c0e249)
| # | Claimed fix | Result |
|---|---|---|
| 1 | Rate limit: x-vercel-forwarded-for → x-real-ip → rightmost XFF |
Pass — trustedClientIp at route.ts:76 never uses leftmost client XFF. Matches Vercel platform header trust. |
| 2 | Recursive stripTenantLike on responses |
Fail (export path) — Non-export JSON is stripped (route.ts:431–434). Successful export responses stream upstream.body raw at route.ts:409–413 and skip stripping entirely. |
| 3 | Path IDs decode then strict allowlist | Pass — sanitizePathSegment rejects .., %2e, %2f, %5c, backslash, NUL, double-encoding before SEGMENT_ALLOW. |
| 4 | Copy gate clean | Pass — Copy gate CI SUCCESS on this head; changed strings have no banned sample/demo wording; EPCIS labeled Preview; no FDA claims added. |
| 5 | Export preview GETs allowlisted + safeHref |
Pass (routing) — Both GETs allowlisted (route.ts:194–195); client csvExportHref / epcisExportHref use safeHref(servicePath(...)) under /api/inflow-lab-demo. |
Blocking
Successful exports bypass tenant stripping, including JSON-LD. The public UI advertises “EPCIS JSON-LD” (InflowLabClient.tsx), but the export branch always streams the upstream body when upstream.ok. In-memory execution of this exact route with a mocked application/ld+json EPCIS response reflected tenant_id and nested customer_id unchanged to the anonymous caller. CSV/text/* streaming is fine; JSON / JSON-LD export bodies must go through recursive stripTenantLike (or an allowlist) before reflection. Also note JSON_CONTENT_TYPES omits application/ld+json, so a non-stream JSON-LD path would currently mis-classify. Add a regression test with nested tenant-like fields in an EPCIS JSON/JSON-LD body.
Unchanged (confirmed)
requireProxyAuthon/api/inflow-labintact/api/inflow-labroute not weakened vs prior head- CSRF via
fetchWithCsrf+ public-routeenforceCsrfintact REGENGINE_ENABLE_INFLOW_LAB_DEMO_PROXYoff by default in production (publicProxyEnabled)
CI note
Required code checks otherwise green on this head (Unit Tests, Copy gate, Secrets, SAST, Security Audit, Frontend CI Status Gate, PR Gate, Vercel Preview, Head SHA Check Guard). Commit Message Check fails only on the earlier Copilot commit subject (fix copy gate findings in demo proxy) — not a code defect; squash with a conventional title. Engine Review Pass waiting on non-author PASS. Do not arm until the export JSON strip is fixed.
REVIEW: FAIL codex 986f32a
Buffer JSON and JSON-LD export preview bodies with a size cap, run them through stripTenantLike before reflecting to anonymous callers, and leave CSV and other non-JSON exports streaming unchanged. Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com>
PetrefiedThunder
left a comment
There was a problem hiding this comment.
Verdict: PASS (delta re-review)
Head reviewed: 5bb0c3eebd017199e0e3198ba7f19089ca6c5397 (exact match; merge-tree equals head tree e6788abe; linear parent 986f32a8).
Authorship: Christopher / PetrefiedThunder. Not codex-authored. Review proceeded.
Prior FAIL item (5750485467 @ 986f32a)
| # | Claimed fix | Result |
|---|---|---|
Export JSON/JSON-LD bypassed stripTenantLike |
Buffer JSON / JSON-LD exports (application/ld+json in JSON_CONTENT_TYPES), run stripTenantLike, 2 MiB fail-closed |
Pass — route.ts export branch: arrayBuffer → size check → parse → stripTenantLike → reflect. Oversized → 502 EXPORT_TOO_LARGE; invalid JSON → 502 UPSTREAM_BAD_RESPONSE. Neither branch attaches upstream.body or partial upstream bytes. |
This delta verify
- JSON / JSON-LD buffered + strip — Pass (
route.tsexport path +MAX_EXPORT_JSON_BYTES = 2 MiB;ld+jsonrecognized). - Cap fails closed / no partial leak — Pass — client never receives upstream export bytes on oversize or bad JSON.
- Regression test — Pass —
inflow-lab-demo-proxy.test.tsasserts nestedtenant_id/tenantId/customer_idstripped fromapplication/ld+json; sentinels only (should-vanish,also-vanish,c1,L1); public GS1@context; no invented production IDs. - CSV cannot emit tenant columns — Pass — proxy still streams CSV/XML as-is (no proxy column filter), but upstream Inflow Lab
app/fda_export.pywrites viacsv.DictWriter(..., fieldnames=FDA_EXPORT_COLUMNS)with a fixed allowlist (TLC, Event Type, Product Description, qty/UoM, locations, dates, reference docs only). Notenant_id/customer_id/ org columns inFDA_EXPORT_COLUMNS; writerow only maps those keys. - Delta scope — Pass — only
frontend/src/app/api/inflow-lab-demo/[...path]/route.tsandfrontend/src/__tests__/api/inflow-lab-demo-proxy.test.ts(+83/−2).
Protected controls (unchanged vs 986f32a)
- Rate limit /
trustedClientIporder:x-vercel-forwarded-for→x-real-ip→ rightmost XFF REGENGINE_ENABLE_INFLOW_LAB_DEMO_PROXYoff by default in productionrequireProxyAuthon/api/inflow-labintact- Client
safeHref+fetchWithCsrf; public CSRF enforcement intact
Non-blocking
- Cap is applied after full
arrayBuffer()read (reflection bounded; memory not incrementally cancelled). Prefer chunked read+abort if DoS hardening is desired later. - No separate test for bare
application/jsonexport, oversize, or malformed JSON (JSON-LD strip coverage is present).
CI
Required code gates green on this head (Unit Tests, Copy gate, SAST, Security Audit, Frontend CI Status Gate, PR Gates, Vercel Preview, Head SHA Check Guard / status). Commit Message Check still fails on earlier Copilot subject — squash-only, not a code defect. Engine Review Pass waiting on this non-author PASS.
REVIEW: PASS codex 5bb0c3e

Why
Prod diagnosis (
/workspace/devops/inflow-lab-401-diagnosis-2026-11-03.md):/tools/inflow-labreturns 200, but every/api/inflow-lab/*call from the standalone client hitsrequireProxyAuth→401 Unauthorized: no valid credentials providedwhen there is no RegEngine session. Starfish demo needs the simulator path without signing in.Base
Branched from latest
origin/main(9b8551d5), which already includes the squash-merge of #3795 (InflowLabClient + tools page restore). Did not stack onclaude/inflow-lab-restore— the page/client already exist on main after #3795 landed. This PR only adds the public demo proxy and points the client at it.Security design
New route only:
frontend/src/app/api/inflow-lab-demo/[...path]/route.ts.requireProxyAuth/ opaque session. Existing/api/inflow-labandrequireProxyAuthuntouched for dashboard/tenant paths.GEThealthz, simulate/status, events, lineage/:lot;POSTsimulate/start|stop|reset, demo-fixtures/:id/load. Everything else → 404 without contacting upstream (no export, scenarios, admin, etc.).REGENGINE_ENABLE_INFLOW_LAB_DEMO_PROXY=1required in production (404 otherwise). Local/test enabled automatically.verifyCsrfToken(double-submit). Not added toCSRF_EXEMPT_RULES. Client already usesfetchWithCsrf. Middleware CSRF also applies for browser traffic./api/client-errors).content-type+ server-heldINFLOW_LAB_BASIC_AUTH_*). Never forward Cookie, Authorization, or caller tokens.credentials: "omit". Nox-regengine-tenant.Client
InflowLabClientINFLOW_API→/api/inflow-lab-demo. Workbench / CSV evaluate paths unchanged.Tests
inflow-lab-demo-proxy.test.ts: unauthenticated allowlisted success; non-allowlisted 404; cross-origin 403; missing CSRF 403; cookies/auth not forwarded + tenant fields stripped; rate limit 429; prod flag 404. Existing authed proxy tests still pass.DevOps env (names only — no values)
REGENGINE_ENABLE_INFLOW_LAB_DEMO_PROXYINFLOW_LAB_SERVICE_URL,INFLOW_LAB_BASIC_AUTH_USERNAME,INFLOW_LAB_BASIC_AUTH_PASSWORDINFLOW_LAB_DEMO_RATE_MAX,INFLOW_LAB_DEMO_RATE_WINDOW_MSNo merge / no deploy from this PR.
Note
Medium Risk
Introduces a new unauthenticated proxy surface to the Inflow Lab backend; risk is mitigated by strict allowlisting, CSRF, rate limits, and prod gating, but misconfiguration or upstream data leakage via responses still warrants careful review.
Overview
Adds a session-free Inflow Lab API path so
/tools/inflow-labworks without RegEngine login (replacing 401s from the authed/api/inflow-labproxy).New catch-all route
/api/inflow-lab-demoforwards only allowlisted simulator/export-preview calls toINFLOW_LAB_SERVICE_URLwith server-side Basic auth when configured. It does not userequireProxyAuth; caller cookies/tokens are never forwarded. Controls include same-origin checks, CSRF on POSTs, per-IP rate limits (trusted IP headers), path-segment hardening, tenant/customer field stripping on JSON/query params, and a production opt-in viaREGENGINE_ENABLE_INFLOW_LAB_DEMO_PROXY.InflowLabClientnow usesINFLOW_API = "/api/inflow-lab-demo"so simulator and export preview links hit the public proxy; workbench paths are unchanged..env.exampledocuments the new flag.inflow-lab-demo-proxy.test.tscovers allowlist, CSRF, cross-origin, rate limit, traversal, and prod-disabled behavior.Reviewed by Cursor Bugbot for commit 5bb0c3e. Bugbot is set up for automated code reviews on this repo. Configure here.