Skip to content

Instantly share code, notes, and snippets.

@Uninen
Created February 24, 2025 20:03
Show Gist options
  • Save Uninen/46df44f4307d324682dabb7aa6e10a34 to your computer and use it in GitHub Desktop.
Save Uninen/46df44f4307d324682dabb7aa6e10a34 to your computer and use it in GitHub Desktop.
Claude 3.7 Sonnet Reasoning a Bug Fix

the post-comments.ts Astro api endpoint returns a 500 in production on cloudflare pages. the response is always empty so the script probably crashes before even sending the response.

the error in the logs is about importing a library that doesn't exist in the project which makes me think there's a deeper node import in some dependency that crashes the process.

this is the cf pages logs entry of a post commenting request that returns a 500. there is no 'pages/api/path' mentioned anywhere in the project code:

{ "truncated": false, "executionModel": "stateless", "outcome": "ok", "scriptVersion": { "id": "9fb8df82-d328-467e-80a3-eb6ec5df5917" }, "scriptName": "pages-worker--5216587-production", "diagnosticsChannelEvents": [], "exceptions": [], "logs": [ { "message": [ "00:33:28 [ERROR] Error: No such module "pages/api/path".\n imported from "pages/api/post-comment.astro.mjs"" ], "level": "error", "timestamp": 1740184408188 } ], "eventTimestamp": 1740184408188, "event": { "request": { "url": "https://[domain-redacted]/api/post-comment", "method": "POST", "headers": { "accept": "/", "accept-encoding": "gzip, br", "accept-language": "en-US,en;q=0.5", "cf-connecting-ip": "178.55.41.120", "cf-connecting-o2o": "1", "cf-ipcountry": "FI", "cf-ray": "915ae2871979abd4", "cf-visitor": "{"scheme":"https"}", "connection": "Keep-Alive", "content-length": "21", "content-type": "application/json", "cookie": "REDACTED", "dnt": "1", "host": "[domain-redacted]", "origin": "https://[domain-redacted]", "priority": "u=0", "referer": "https://[domain-redacted]/2025/helmikuu/kielimallien-etiikka/", "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-fetch-site": "same-origin", "sec-gpc": "1", "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:135.0) Gecko/20100101 Firefox/135.0", "x-forwarded-for": "178.55.41.120", "x-forwarded-proto": "https", "x-real-ip": "178.55.41.120" }, "cf": { "requestHeaderNames": {}, "httpProtocol": "HTTP/3", "tlsCipher": "AEAD-AES128-GCM-SHA256", "continent": "EU", "asn": 16086, "clientAcceptEncoding": "gzip, br", "country": "FI", "isEUCountry": "1", "verifiedBotCategory": "", "requestPriority": "", "tlsClientAuth": { "certIssuerDNLegacy": "", "certIssuerSKI": "", "certSubjectDNRFC2253": "", "certSubjectDNLegacy": "", "certFingerprintSHA256": "", "certNotBefore": "", "certSKI": "", "certSerial": "", "certIssuerDN": "", "certVerified": "NONE", "certNotAfter": "", "certSubjectDN": "", "certPresented": "0", "certRevoked": "0", "certIssuerSerial": "", "certIssuerDNRFC2253": "", "certFingerprintSHA1": "" }, "tlsClientExtensionsSha1": "", "tlsVersion": "TLSv1.3", "colo": "CPH", "timezone": "Europe/Helsinki", "region": "Uusimaa", "longitude": "25.12740", "edgeRequestKeepAliveStatus": 1, "tlsClientRandom": "", "latitude": "60.29390", "postalCode": "01260", "city": "Vantaa", "regionCode": "18", "asOrganization": "DNA Oyj", "tlsClientHelloLength": "", "pagesHostName": "redacted.pages.dev", "botManagement": { "corporateProxy": false, "verifiedBot": false, "jsDetection": { "passed": false }, "staticResource": false, "detectionIds": {}, "score": 99 } } }, "response": { "status": 500 } }, "id": 1 }

can you analyze possible problematic lines or imports in the post-comments.ts and its depenencies


Based on the error log, the issue is occurring in the Cloudflare Pages environment with the post-comment endpoint. The specific error is:

Error: No such module "pages/api/path". imported from "pages/api/post-comment.astro.mjs"

This suggests there's a dependency resolution problem after the Astro build process compiles your TypeScript file into JavaScript for deployment.

Main Problem Areas

Looking at your post-comment.ts file and its dependencies, the most likely culprit is the combination of these dependencies:

  1. JSDOM and DOMPurify in src/lib/server.ts:
    import createDOMPurify from 'dompurify'
    import { JSDOM } from 'jsdom'
    import { marked } from 'marked'

These libraries are particularly problematic for Cloudflare Pages because:

  • JSDOM is a complex Node.js-based library that emulates a browser environment
  • Cloudflare Pages/Workers run in a V8 isolate (not a full Node.js environment)
  • These types of Node-specific libraries often have implicit dependencies that don't work in edge environments

Recommended Solutions

  1. Remove Node-specific dependencies:

    The simplest approach would be to replace JSDOM and DOMPurify with more edge-compatible alternatives:

    // src/lib/server.ts - simplified version
    import { supabase } from '@/lib/supabase'
    import { marked } from 'marked' // Marked might work alone
    
    // Simple sanitizer function
    function simpleSanitize(html: string) {
      // Basic sanitization - replace with more robust solution if needed
      return html
        .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
        .replace(/onclick/gi, 'data-onclick')
        // Add more replacements as needed
    }
    
    export async function createComment(url: string, content: string) {
      const { data: { user } } = await supabase.auth.getUser()
      if (!user) return null
    
      // Use marked alone or with simple sanitization
      const html = simpleSanitize(await marked.parse(content))
    
      // Rest of your function...
    }
  2. Move HTML processing to the client side:

    An alternative approach is to store the raw markdown content and handle rendering and sanitization in the browser:

    export async function createComment(url: string, content: string) {
      // Store raw content
      const { data, error } = await supabase
        .from('comments')
        .insert({
          user_id: user.id,
          userprofile_id: user.user_metadata.userprofile_id,
          content: content,
          content_html: content, // Store unprocessed or do minimal processing
          post_url: url,
        })
        .select()
      
      // ...
    }

    Then render and sanitize in your frontend components.

  3. Use Cloudflare-compatible packages:

    Look for sanitization libraries specifically designed for edge environments. For example, some lightweight packages like isomorphic-dompurify might work better in this environment.

The error about "pages/api/path" suggests that during the bundling process, one of these Node.js dependencies is trying to import a module that doesn't exist in the edge environment. By replacing these dependencies with edge-compatible alternatives, you should be able to resolve the issue.

Would you like me to help implement any of these solutions or provide more specific code examples?

@Uninen
Copy link
Author

Uninen commented Feb 24, 2025

I also asked Claude to write me tests for this. I had to fix just one line (where it forgot p-tags), but otherwise I like the tests (one-shot!):

import { describe, expect, it } from 'vitest'
import { sanitizeMarkdown } from '../server'

describe('sanitizeMarkdown', () => {
  it('should convert markdown to HTML and sanitize it', async () => {
    const markdown = `
# Test Heading
[Test Link](https://example.com)
**Bold Text** and *Italic Text*
<script>alert('xss')</script>
<p class="dangerous">Safe Text</p>
`

    const result = await sanitizeMarkdown(markdown)

    // Should preserve allowed tags and attributes
    expect(result).toContain('<a href="https://example.com">')
    expect(result).toContain('<strong>Bold Text</strong>')
    expect(result).toContain('<em>Italic Text</em>')

    // Should strip dangerous tags and attributes
    expect(result).not.toContain('<script>')
    expect(result).not.toContain('class="dangerous"')

    // Should keep the text content
    expect(result).toContain('Test Heading')
    expect(result).toContain('Safe Text')
  })

  it('should handle empty input', async () => {
    const result = await sanitizeMarkdown('')
    expect(result).toBe('')
  })

  it('should sanitize malicious href attributes', async () => {
    const markdown = '[Click me](javascript:alert("xss"))'
    const result = await sanitizeMarkdown(markdown)
    expect(result).toContain('<p><a>Click me</a></p>')
  })
})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment