Skip to content

Instantly share code, notes, and snippets.

@AbhiShake1
Created April 7, 2026 07:29
Show Gist options
  • Select an option

  • Save AbhiShake1/69ae4ed2f6993a6f276678a8b9d8643e to your computer and use it in GitHub Desktop.

Select an option

Save AbhiShake1/69ae4ed2f6993a6f276678a8b9d8643e to your computer and use it in GitHub Desktop.
Ship Fast Bug Reports (2026-04-07)

Ship Fast API & Network Bug Report

Date: 2026-04-07 Target: http://localhost:7420/ Note: The app uses Express. The actual generation endpoint is /api/sessions (POST), not /api/generate.


Test Results

Test 1: POST /api/generate without auth

curl -v http://localhost:7420/api/generate -X POST -H "Content-Type: application/json" -d '{"prompt":"test"}' 2>&1

Result: 404 Not Found — Cannot POST /api/generate Notes: This endpoint does not exist. The real generation endpoint is POST /api/sessions.


Test 2: Malformed JSON to /api/generate

curl -v http://localhost:7420/api/generate -X POST -H "Content-Type: application/json" -d '{broken json' 2>&1

Result: 400 Bad Request BUG - STACK TRACE LEAK: The response exposes a full Node.js stack trace including:

  • Internal file paths: /Users/abhi/proj/sensei/ship-fast/node_modules/body-parser/lib/types/json.js:92:19
  • node_modules/raw-body/index.js:238:16
  • Internal Node.js modules and line numbers

This reveals the server's filesystem layout to attackers.


Test 3: Empty body POST to /api/generate

curl -v http://localhost:7420/api/generate -X POST 2>&1

Result: 404 Not Found — Cannot POST /api/generate


Test 4: GET /api/generate

curl -v http://localhost:7420/api/generate 2>&1

Result: 404 Not Found — Cannot GET /api/generate


Test 5: Exposed env/config endpoints

curl -s http://localhost:7420/.env

Result: 404 — .env not exposed. Good.

curl -s http://localhost:7420/api/config

Result: 200 — Returns Firebase client config:

{
  "apiKey": "AIzaSyDu1_UIcdl-r0pdf0IYPXCnYE-Sl5rJbo4",
  "authDomain": "ship-fast-saas.firebaseapp.com",
  "projectId": "ship-fast-saas",
  "appId": "1:359066355652:web:62b67a9496716e4d004691"
}

Notes: Firebase client config is intentionally public (required for client-side auth). This is expected behavior, not a bug. The code comment says // Public: Firebase client config.

curl -s http://localhost:7420/api/health

Result: 404 — No health endpoint. Not a bug, but could be useful for monitoring.

curl -s http://localhost:7420/.git/config

Result: 404 — .git not exposed. Good.

curl -s http://localhost:7420/package.json

Result: 404 — Not exposed. Good.


Test 6: Rate limiting test (20 rapid requests)

Against /api/generate:

for i in $(seq 1 20); do curl -s -o /dev/null -w "%{http_code}" http://localhost:7420/api/generate -X POST -H "Content-Type: application/json" -d '{"prompt":"test site"}'; echo " request $i"; done

Result: All 20 returned 404 (endpoint doesn't exist).

Against /api/sessions (the real endpoint):

for i in $(seq 1 20); do curl -s -o /dev/null -w "%{http_code}" http://localhost:7420/api/sessions -X POST -H "Content-Type: application/json" -d '{"prompt":"A test site for rate limit verification with enough characters to pass the minimum requirement for prompt length"}'; echo " request $i"; done

Result: All 20 returned 200. No rate limiting triggered.

Analysis: Rate limits are intentionally skipped for localhost/development via isLocalDevelopmentRequest() which returns true when NODE_ENV !== 'production' and the host is localhost. In production (NODE_ENV=production), rate limits ARE enforced:

  • Anonymous: daily IP limit (2-3/day), 10-min IP limit, concurrent limit
  • Authenticated: monthly cap, 5/10min user limit, IP limit, concurrent limit

Not a production bug, but worth noting that dev mode has zero rate limiting.


Test 7: CORS headers

curl -v -X OPTIONS http://localhost:7420/api/generate -H "Origin: http://evil.com" -H "Access-Control-Request-Method: POST" 2>&1

Result: 404 (endpoint doesn't exist).

Against /api/sessions: Result: 200 OK, but no Access-Control-Allow-Origin header returned.

BUG - MISSING CORS CONFIGURATION: No CORS headers are set on any API response. The OPTIONS preflight returns 200 but without Access-Control-Allow-Origin, Access-Control-Allow-Methods, or Access-Control-Allow-Headers. This means:

  • Cross-origin requests from the frontend (if hosted on a different domain) would fail
  • However, since the app appears to be SSR (same-origin), this may be by design
  • No cors middleware is installed (not found in server code)

The lack of CORS headers also means external sites CANNOT make cross-origin API calls, which is actually good security. However, if the frontend ever moves to a separate domain, this would break.


Test 8: Payment/billing endpoints

curl -s http://localhost:7420/api/payments
curl -s http://localhost:7420/api/webhook
curl -s http://localhost:7420/api/stripe

Result: All return 404 — Not exposed via GET. Good. Notes: Payment logic exists in src/server/payments.js but is likely mounted under different routes or only accepts specific methods.


Test 9: Server info leakage in headers

curl -sI http://localhost:7420/

Response headers:

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: private, no-store, max-age=0, must-revalidate
Pragma: no-cache
ETag: W/"5370-UkshlQFaWqHJDy06kfithv2mluk"
X-Powered-By: Express
X-SF-Home-Source: ssr

BUG - SERVER INFO LEAKAGE:

  1. X-Powered-By: Express — Reveals the server framework. Express recommends disabling this with app.disable('trust proxy') or using helmet. Should use app.disable('x-powered-by').
  2. X-SF-Home-Source: ssr — Custom header exposing rendering strategy. Minor info leak.
  3. No Strict-Transport-Security header — Missing HSTS.
  4. No X-Frame-Options header — Page can be embedded in iframes (clickjacking risk).

Good: X-Content-Type-Options: nosniff IS present on API routes. Content-Security-Policy is present on error pages. X-Robots-Tag: noindex is on API routes.


Test 10: Gallery/preview API

curl -s http://localhost:7420/api/gallery
curl -s http://localhost:7420/api/preview

Result: Both return 404 — Not exposed. Good.


Additional Tests (real endpoints without auth)

curl -s http://localhost:7420/api/sessions

Result: {"error":"Unauthorized"} — Properly protected with requireAuth.

curl -s http://localhost:7420/api/sessions/recent

Result: 200 — Returns recent sessions data (session IDs, prompts, deployment URLs, timing data). POTENTIAL ISSUE: This endpoint has no auth middleware and returns all recent sessions including prompt text. Could be intentional for a public gallery feature, but exposes user prompts.

curl -s http://localhost:7420/api/credits
curl -s http://localhost:7420/api/subscription-status

Result: Both return {"error":"Unauthorized"} — Properly protected.

curl -s http://localhost:7420/api/early-adopter-status

Result: 200 — Returns {"eligible":true,"slotsRemaining":500,"totalSlots":500,"priceId":"price_1TGmRlP46NVnr2Hth4gNOJdI"}. INFO LEAK: Exposes Stripe price ID and slot availability. The Stripe price ID itself is semi-public (needed for checkout), but the slot count reveals business metrics.

curl -s http://localhost:7420/api/sessions -X POST -H "Content-Type: application/json" -d '{"prompt":"test"}'

Result: 400 — {"error":"Please provide a meaningful description of the website you want to build."}. Good input validation.


Summary of Bugs Found

Critical

# Bug Severity Details
1 Stack trace leak on malformed JSON HIGH Full Node.js stack trace with filesystem paths exposed in 400 response body. Fix: add custom error handler for JSON parse errors.

Medium

# Bug Severity Details
2 X-Powered-By: Express header exposed MEDIUM Reveals server framework. Fix: app.disable('x-powered-by') or use helmet.
3 Missing security headers MEDIUM No HSTS, no X-Frame-Options, no global CSP. Consider adding helmet middleware.
4 /api/sessions/recent unauthenticated MEDIUM Exposes recent session prompts and deployment URLs without auth. May be intentional (gallery), but worth reviewing.

Low

# Bug Severity Details
5 Stripe price ID in /api/early-adopter-status LOW Exposes pricing internals. Minor — price IDs are semi-public.
6 No CORS middleware LOW No cross-origin headers set. Currently safe (same-origin SSR), but would break if frontend is ever separated.
7 X-SF-Home-Source: ssr header LOW Minor info leak revealing rendering strategy.

Not Bugs (confirmed working correctly)

  • .env, .git/config, package.json — all properly blocked (404)
  • /api/sessions, /api/credits, /api/subscription-status — properly require auth
  • Rate limiting — works in production mode; correctly bypassed in dev
  • Input validation — rejects short/gibberish prompts
  • Content policy — checks prompts before generation
  • Payment endpoints — not exposed via GET

Ship Fast Navigation & Auth Bug Report

Tested: http://localhost:7420/
Date: 2026-04-07
Branch: develop


BUG 1 (HIGH) - Placeholder animation pollutes input value

The animated placeholder text ("Build me a website", "A premium architectur...", etc.) is being written into the actual <input> / <textarea> value rather than displayed as a CSS placeholder or overlay. On page load, the input field accumulates hundreds of repetitions of "Build me a website " in its .value property. This means:

  • If a user starts typing without clicking first, their text gets appended to the ghost text
  • The GENERATE button logic may be confused by the pre-filled value
  • The field appears to have user input when it does not

Screenshot: /tmp/ship-fast-bugs/gallery-rapid.png (shows XSS test text mixed with placeholder)
Evidence: document.querySelector('textarea').value returns "Build me a website" repeated 500+ times


BUG 2 (MEDIUM) - No custom 404 page; Express default error leaks framework info

All invalid routes (/admin, /api/users, /etc/passwd, /api/generate via GET) return the raw Express error:

Cannot GET /admin

This leaks that the server uses Express.js. A custom 404 page should be served instead, ideally redirecting to the homepage or showing a branded error page.

Screenshots: /tmp/ship-fast-bugs/admin-1.png, /tmp/ship-fast-bugs/api-users.png, /tmp/ship-fast-bugs/path-traversal.png, /tmp/ship-fast-bugs/api-generate.png


BUG 3 (LOW) - Gallery item shows $0.0000 cost

One gallery item displays a cost of $0.0000 with no generation time shown, suggesting a test/debug entry leaked into the public gallery. Other entries show realistic costs like $0.0755, $0.0988, etc.


BUG 4 (LOW) - Console errors accumulating

The agent-browser errors command detected 5-14 console errors across pages. The errors appear on both the homepage and the pricing page. The exact error messages were not captured in detail by the tool (shown as empty lines), but their presence indicates JavaScript issues running in the background.


BUG 5 (INFO) - Gallery has only NEXT, no PREVIOUS button

The gallery pagination only has a "NEXT" button with no "PREVIOUS" button. Users cannot go back to see earlier gallery items once they paginate forward. The rapid-click test (5 clicks) did not cause visible crashes or race conditions, which is good.


WORKING CORRECTLY

Feature Status Notes
SIGN IN button OK Opens modal with Google, GitHub, email/password options
PRICING (top nav) OK Navigates to /pricing, shows tiered pricing in INR
PRIVACY link OK Navigates to /privacy, shows full privacy policy
Path traversal SAFE /../../../etc/passwd normalized to /etc/passwd, returns 404
/api/generate GET OK Returns 404 (POST-only endpoint as expected)
Back/forward nav OK Browser back button preserves page state
Footer links OK Website -> liviogama.com, LinkedIn -> linkedin.com/in/livio-gamassia, X -> x.com/LivioGama (all open in new tab)
Gallery rapid clicks OK No crashes or race conditions from 5 rapid NEXT clicks

Screenshots Index

File Description
auth-1.png Sign in modal
pricing-1.png Homepage after footer pricing click
pricing-2.png Pricing page via top nav
privacy-1.png Privacy policy page
gallery-rapid.png Gallery after 5 rapid NEXT clicks
admin-1.png /admin 404
api-users.png /api/users 404
path-traversal.png Path traversal attempt 404
api-generate.png /api/generate GET 404
back-state.png Homepage after browser back
footer-links.png Full page with footer

Ship Fast - Responsive Design, Performance & Edge Case Report

Tested: 2026-04-07 URL: http://localhost:7420/


1. Mobile Viewport (375x812 - iPhone)

Screenshot: mobile-home.png

Findings:

  • Nav is usable: PRICING link and SIGN IN button are accessible
  • Text input (prompt textarea) is reachable and functional
  • GENERATE button is visible and accessible (disabled until input is provided)
  • Gallery cards display in a single column (343px wide) -- correct responsive behavior
  • NEXT pagination button is accessible at the bottom of the gallery

Verdict: PASS -- Mobile layout is functional


2. Very Small Viewport (320x480)

Screenshots: tiny-viewport.png, tiny-viewport-above-fold.png, tiny-viewport-scrolled.png, tiny-viewport-full.png

Findings:

  • BUG (Minor): At 320x480, the GENERATE button is below the fold. Users must scroll to find it. The prompt textarea is partially cut off at the viewport bottom edge.
  • BUG (Minor): When scrolling down, the fixed nav bar (position: fixed, z-index: 210, top: 14px) overlaps with the page heading text ("FAST" appears behind the PRICING/SIGN IN buttons). No background blur or solid background on the nav to separate it from content behind.
  • Grid properly collapses to single column (288px) at 320px -- good
  • No horizontal overflow detected (body scroll width matches viewport)
  • All interactive elements remain accessible via scrolling

Verdict: MINOR ISSUES -- Usable but nav overlap is visually jarring


3. Very Large Viewport (2560x1440)

Screenshot: large-viewport.png

Findings:

  • Gallery grid displays in a 3-column layout at 2560px -- appropriate use of space
  • Content is centered with reasonable max-widths
  • No excessive whitespace or broken layouts
  • All cards render with iframes visible
  • Footer is properly positioned at the bottom

Verdict: PASS -- Large viewport renders well


4. JavaScript Disabled / Noscript Fallback

Findings:

  • BUG (Medium): No <noscript> fallback content found. If JavaScript is disabled, the page would render nothing useful since the site is client-side rendered.
  • BUG (Medium): No <meta name="viewport"> tag detected. While the CSS appears to handle responsive layouts via media queries or container queries, the missing viewport meta tag means mobile browsers may render the page at desktop width and scale down, making text tiny and unusable on real mobile devices. This was confirmed by document.querySelector('meta[name=viewport]') returning null.

5. Broken Images and Missing Resources

Findings:

  • No broken images detected (all img elements have complete === true and naturalWidth > 0)
  • Zero <img> tags on the main page -- site uses iframes for gallery previews instead of thumbnail images
  • NOTE: No lazy loading (loading="lazy") is used on any images (0 found). Since there are no images, this is not a bug, but if images are added later, lazy loading should be considered.

Verdict: PASS (no images to break)


6. Iframe Loading and Rendering

Findings:

  • 12 total iframes on page 1
  • 6 iframes have valid src URLs (e.g., http://localhost:7420/preview/{hash}/)
  • BUG (Medium): 6 iframes have empty src attributes. These are likely placeholder/lazy-loaded iframes for gallery items below the fold, but they have no src at all -- they render as blank boxes. This means half the gallery cards show no preview content.
  • All iframes with valid src report loaded: true

Verdict: BUG -- Half the iframes are empty


7. Performance

Findings:

  • Excellent performance. No slow resources (>500ms) detected.
  • Total transfer size: ~4 KB for 17 resources
  • Time to first byte: 3ms (localhost)
  • DOM Content Loaded: 7ms
  • Full load: 7ms
  • Protocol: HTTP/1.1 (not HTTP/2 -- acceptable for localhost)
  • DOM node count after gallery navigation (10 NEXT clicks): 296 nodes -- lightweight

Verdict: PASS -- Very fast


8. Memory Leak Check (Gallery Navigation)

Findings:

  • performance.memory API returns empty object (not available in this Chrome configuration)
  • After clicking NEXT 10 times:
    • DOM nodes: 296 (reasonable)
    • Iframes: 12 (consistent, not growing)
    • Detached iframes: 0 (no leaks)
    • No runaway DOM growth observed
  • Gallery navigation appears to properly replace content rather than accumulating it

Verdict: PASS -- No evidence of memory leaks


9. Accessibility

Findings:

  • Images without alt text: 0 (no images on page)
  • Buttons without labels: 0
  • Inputs without labels: 0
  • <html lang> attribute: Present
  • Navigation landmarks: Proper <nav> elements with labels ("Primary", "Gallery pages", "Legal")
  • Heading hierarchy: h1 ("Ship Fast AI website generator") followed by h2 ("SEE WHAT OTHER SPEEDSTERS GENERATED")
  • Textarea has proper placeholder text ("Describe the website you want to build") and required attribute

Verdict: PASS -- Good accessibility baseline


Summary of Bugs Found

# Severity Description
1 Medium Missing <meta name="viewport"> tag -- mobile browsers will render at desktop width and zoom out, making the site unusable on real mobile devices without pinch-to-zoom
2 Medium 6 of 12 gallery iframes have empty src attributes, showing blank preview cards
3 Medium No <noscript> fallback -- page is entirely non-functional without JavaScript
4 Minor Fixed nav bar overlaps heading text when scrolling on small viewports (320px) -- no opaque/blurred background to separate nav from content
5 Minor At 320x480, the GENERATE button is below the fold and the textarea is partially cut off at the viewport edge

Priority Recommendation

  1. Fix viewport meta tag immediately -- this is the most impactful bug as it breaks the mobile experience on real devices
  2. Fix empty iframe src attributes for complete gallery rendering
  3. Add noscript fallback for SEO and graceful degradation
  4. Add background to fixed nav for visual separation on scroll

Ship Fast Security & Edge-Case Test Report

Date: 2026-04-07 Target: http://localhost:7420/ (Ship Fast - AI Website Generator) Tester: Automated agent-browser


Test Results Summary

# Test Result Severity
1 XSS Injection PASS - properly escaped None
2 Very Long Input (10,000 chars) PARTIAL BUG - no client-side limit Low
3 SQL Injection PASS - treated as text prompt None
4 Empty/Whitespace Input PASS - button stays disabled None
5 Template Injection PASS - treated as text prompt None
6 Emoji Flood (500 emojis) PASS - accepted, no crash None

Detailed Findings

Test 1: XSS Injection

Payload: <script>alert('XSS')</script> Build me a portfolio website with contact form and gallery Result: The payload was accepted and a website was generated. The raw <script> tags were properly HTML-escaped in the gallery listing (&lt;script&gt;). No JavaScript execution occurred. The generated website content was rendered in a sandboxed iframe. Verdict: PASS. XSS is properly mitigated via HTML escaping.

Test 2: Very Long Input (10,000 characters)

Payload: "Build me a website " repeated to 10,000 characters Result: The client-side accepted the full 10,000-character input with no restriction. The GENERATE button became enabled. The server has a MAX_PROMPT_LENGTH = 5000 check in src/contracts/http-contracts.js, but there is NO client-side maxlength attribute on the textarea and NO client-side length validation. Bug Found: Missing client-side max length enforcement on #prompt-input. Users can type/paste arbitrarily long text. While the server will reject it (>5000 chars), this creates a poor UX -- users won't know about the limit until after submission. Recommendation: Add maxlength="5000" to the textarea element, or add a character counter with client-side validation in syncSubmitButtonState().

Test 3: SQL Injection

Payload: '; DROP TABLE users; -- Build me a website for a database admin tool with SQL query editor interface Result: Accepted and processed. A website was generated at /session/a24562d35632. The SQL injection syntax was treated as literal text in the AI prompt. No database queries appear to use raw user input. Verdict: PASS. No SQL injection vulnerability. The prompt is passed to an AI model, not interpolated into SQL.

Test 4: Empty/Whitespace Input

Payload: 100 space characters Result: The GENERATE button correctly remained disabled. The syncSubmitButtonState() function checks input.value.trim().length < MIN_PROMPT_LENGTH (where MIN_PROMPT_LENGTH = 70). Direct form submission dispatch was also properly blocked. Verdict: PASS. Whitespace-only input is correctly rejected on the client side.

Test 5: Template Injection

Payload: {{constructor.constructor('return this')()}} Build me a website for testing template injection security vulnerabilities Result: Accepted and processed. A website was generated at /session/9974e857ced3. The template syntax was treated as literal text. Verdict: PASS. No template injection vulnerability detected.

Test 6: Emoji Flood

Payload: 500 emoji characters (10 unique emojis repeated 50 times) Result: The input was accepted and the GENERATE button was enabled (emoji string length = 1000, which is >= MIN_PROMPT_LENGTH of 70). The form appeared to submit but stayed on the homepage. No crash, no error, no broken layout. Verdict: PASS. Unicode/emoji input handled gracefully.


Additional Observations

  1. MIN_PROMPT_LENGTH = 70 characters: All prompts must be at least 70 characters (after trim). This is enforced client-side.

  2. Content Policy: A robust client-side content policy (/scripts/content-policy.js) blocks harmful content categories including CSAM, violence, drug synthesis, etc. It includes leet-speak normalization and zero-width character stripping.

  3. Authentication: The GENERATE function works for anonymous users (no sign-in required). Generated sites appear in the public gallery.

  4. Gallery XSS Exposure: The XSS payload <script>alert('XSS')</script> appears as visible text in the gallery listing, meaning prompt text IS rendered in the DOM -- but it's properly escaped. This is important because the gallery is visible to all users.

  5. Generated Content in Iframes: All generated websites are displayed inside iframes, providing an additional layer of XSS protection via same-origin policy (assuming proper sandbox attributes).


Bugs Found

BUG-1: No Client-Side Input Length Limit (Low Severity)

  • Location: #prompt-input textarea in the homepage
  • Issue: No maxlength attribute or client-side length validation
  • Server limit: 5000 characters (in src/contracts/http-contracts.js:3)
  • Client limit: Only MIN_PROMPT_LENGTH (70) is enforced; no maximum
  • Impact: Users can type/paste 10,000+ characters, submit, and get a server-side rejection without clear feedback
  • Fix: Add maxlength="5000" to textarea, or add length check in syncSubmitButtonState() in /scripts/homepage.js

Screenshots

File Description
xss-test-1.png XSS payload in textbox
xss-test-1b.png XSS payload with enabled button
xss-result-1.png Page after XSS submission (shows in gallery, escaped)
xss-test-2.png 10,000 char input
xss-result-2.png Result of long input submission
xss-test-3.png SQL injection payload
xss-result-3.png SQL injection generated a website (treated as text)
xss-test-4.png Whitespace-only input (button disabled)
xss-test-5.png Template injection payload
xss-result-5.png Template injection generated a website
xss-test-6.png Emoji flood in textbox
xss-result-6.png Emoji flood result
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment