You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The headless web server (scripts/serve.js) checks path traversal with startsWith(DIST_DIR) but omits the trailing path separator. Any sibling directory whose name begins with dist (e.g. dist-backup/, dist-dev/, distribute/) can be read without authentication.
Root Cause
letfilePath=path.join(DIST_DIR,urlPath)if(!filePath.startsWith(DIST_DIR)){// ← missing + path.sepres.statusCode=403res.end('Forbidden')return}// ...fs.createReadStream(filePath).pipe(res)// line 124
npm install # clones qingchencloud/clawpanel, installs as local dep
npm test# runs poc.js
poc.js resolves the real installed package via require.resolve('clawpanel/package.json'), creates dist/ + dist-secret/credential.txt inside the package root, starts scripts/serve.js from the installed package, and sends a raw TCP GET /../dist-secret/credential.txt:
$ npm test
VULNERABLE
SECRET_API_KEY_DO_NOT_LEAK_12345
The startsWith check blocks /../package.json (resolves outside dist prefix → 403), but passes /../dist-secret/credential.txt (resolves to a path that still starts with .../dist).
Activity