|
| 1 | +const fs = require('fs'); |
| 2 | +const path = require('path'); |
| 3 | + |
| 4 | +const WORKFLOWS_DIR = '.github/workflows'; |
| 5 | +const SERVER_JS = 'rag-agentic-dashboard/server.js'; |
| 6 | +const PKG_JSON = 'rag-agentic-dashboard/package.json'; |
| 7 | + |
| 8 | +// 1. Precise Action Pinning |
| 9 | +const ACTION_MAP = { |
| 10 | + 'actions/checkout': '692973e3d937129bcbf40652eb9f2f61becf3332', |
| 11 | + 'actions/setup-python': 'f677109307c7a44114705603b30e01c0ad72a39d', |
| 12 | + 'actions/setup-node': '1a44421d2379b183610001099a6792610738d8f2', |
| 13 | + 'actions/upload-artifact': '65462800fd760344b1a7b4382951275a0abb4808', |
| 14 | + 'actions/download-artifact': 'fa0a91b85d4f404e444e00e005971372dec800d1', |
| 15 | + 'actions/labeler': '8558fd74291d67161a8a78ce36a881fa63b766a9', |
| 16 | + 'github/super-linter': '4483756a815a5f6e80b27902d3345e54d5b27163', |
| 17 | + 'ludeeus/action-shellcheck': '94e0a5663708a74e508827f311c818816c1416e8', |
| 18 | + 'denoland/setup-deno': '61fe2df320078202e33d7d5ad347e7dcfa0e8f31', |
| 19 | + 'open-policy-agent/setup-opa': '790401b7a0f785501861034177727192667d4e32', |
| 20 | + 'github/codeql-action/init': '23acc5c56da8f1d67c0558b779d201e5d797c271', |
| 21 | + 'github/codeql-action/analyze': '23acc5c56da8f1d67c0558b779d201e5d797c271', |
| 22 | + 'docker/setup-buildx-action': '944597f4a0709b9bc0446465693c7d9e1c15433d', |
| 23 | + 'docker/login-action': 'dd4fa0671be5250ee6f50aedf4cb05514baad2da', |
| 24 | + 'docker/build-push-action': 'ac9327eae2b366085ac7f6a2d02df8aa8ead720a', |
| 25 | + 'actions/configure-pages': '1f0c5cde4bc74c01375badad0f946a4993308d16', |
| 26 | + 'actions/cache': '0c45773b623bec8c7efd44a0f4691c13d78905c1', |
| 27 | + 'actions/upload-pages-artifact': '56afc609e74202658d3ffba0e8f6dee46298ecc2', |
| 28 | + 'actions/deploy-pages': 'd6db9015730510f01c9ca7c21b66236e14d1719c' |
| 29 | +}; |
| 30 | + |
| 31 | +const workflows = fs.readdirSync(WORKFLOWS_DIR).filter(f => f.endsWith('.yml')); |
| 32 | +workflows.forEach(file => { |
| 33 | + let content = fs.readFileSync(path.join(WORKFLOWS_DIR, file), 'utf8'); |
| 34 | + for (const [action, sha] of Object.entries(ACTION_MAP)) { |
| 35 | + const regex = new RegExp(`uses:\\s*${action}(@[^\\s]*)?`, 'g'); |
| 36 | + content = content.replace(regex, `uses: ${action}@${sha}`); |
| 37 | + } |
| 38 | + fs.writeFileSync(path.join(WORKFLOWS_DIR, file), content); |
| 39 | +}); |
| 40 | + |
| 41 | +// 2. server.js Hardening |
| 42 | +let serverContent = fs.readFileSync(SERVER_JS, 'utf8'); |
| 43 | + |
| 44 | +// A. Fix ReDoS by replacing keyword regex with safe inclusion checks |
| 45 | +serverContent = serverContent.replace( |
| 46 | + /if \(\['govern', 'map', 'measure', 'manage'\]\.every\(k => new RegExp\(k, 'i'\)\.test\(text\)\)\)/g, |
| 47 | + "if (['govern', 'map', 'measure', 'manage'].every(k => text.toLowerCase().includes(k)))" |
| 48 | +); |
| 49 | +serverContent = serverContent.replace( |
| 50 | + /if \(\['govern', 'map', 'measure', 'manage'\]\.every\(k => text\.toLowerCase\(\)\.includes\(k\.toLowerCase\(\)\)\)\)/g, |
| 51 | + "if (['govern', 'map', 'measure', 'manage'].every(k => text.toLowerCase().includes(k)))" |
| 52 | +); |
| 53 | + |
| 54 | +// B. Global Rate Limiting and File Access Protection |
| 55 | +// Ensure express-rate-limit is at the top and applied to all routes |
| 56 | +serverContent = serverContent.replace(/const rateLimit = require\('express-rate-limit'\);/g, ''); |
| 57 | +serverContent = serverContent.replace(/const limiter = rateLimit\(\{[\s\S]*?\}\);/g, ''); |
| 58 | +serverContent = serverContent.replace(/app\.use\(limiter\);/g, ''); |
| 59 | + |
| 60 | +const appInitPos = serverContent.indexOf('const app = express();'); |
| 61 | +if (appInitPos !== -1) { |
| 62 | + const insertPos = serverContent.indexOf('\n', appInitPos) + 1; |
| 63 | + const rateLimitBlock = `\nconst rateLimit = require('express-rate-limit');\nconst limiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 100, standardHeaders: true, legacyHeaders: false });\napp.use(limiter);\n`; |
| 64 | + serverContent = serverContent.slice(0, insertPos) + rateLimitBlock + serverContent.slice(insertPos); |
| 65 | +} |
| 66 | + |
| 67 | +// C. Resolve Deno Linting (unused req) |
| 68 | +// We prefix 'req' with '_' in route handlers where 'req' is not used in the body. |
| 69 | +const routeRegex = /app\.(get|post|put|delete)\(['"](.*?)['"],\s*\((req),\s*(res)\)\s*=>/g; |
| 70 | +serverContent = serverContent.replace(routeRegex, (match, method, route, req, res) => { |
| 71 | + // This is a simple heuristic: if the body is just res.json(...) or res.sendFile(...), req is usually unused. |
| 72 | + // Or we can just check if 'req.' exists in the line. |
| 73 | + const lineEndPos = serverContent.indexOf('\n', serverContent.indexOf(match)); |
| 74 | + const line = serverContent.substring(serverContent.indexOf(match), lineEndPos); |
| 75 | + if (!line.includes('req.')) { |
| 76 | + return `app.${method}('${route}', (_req, res) =>`; |
| 77 | + } |
| 78 | + return match; |
| 79 | +}); |
| 80 | + |
| 81 | +fs.writeFileSync(SERVER_JS, serverContent); |
| 82 | + |
| 83 | +// 3. package.json Dependencies |
| 84 | +const pkg = JSON.parse(fs.readFileSync(PKG_JSON, 'utf8')); |
| 85 | +pkg.dependencies['express-rate-limit'] = '^7.5.0'; |
| 86 | +fs.writeFileSync(PKG_JSON, JSON.stringify(pkg, null, 2) + '\n'); |
| 87 | + |
| 88 | +// 4. Netlify Rules Formatting |
| 89 | +const headerContent = "/*\n Cross-Origin-Opener-Policy: same-origin\n Cross-Origin-Embedder-Policy: require-corp\n"; |
| 90 | +const redirectContent = "/api/* /api/:splat 200\n/* /index.html 200\n"; |
| 91 | + |
| 92 | +fs.writeFileSync('_headers', headerContent); |
| 93 | +fs.writeFileSync('_redirects', redirectContent); |
| 94 | +fs.writeFileSync('next-app/public/_headers', headerContent); |
| 95 | +fs.writeFileSync('next-app/public/_redirects', redirectContent); |
| 96 | + |
| 97 | +// 5. DeepSource Config |
| 98 | +const dsContent = `version = 1 |
| 99 | +
|
| 100 | +[[analyzers]] |
| 101 | +name = "python" |
| 102 | +enabled = true |
| 103 | + [analyzers.meta] |
| 104 | + runtime_version = "3.x" |
| 105 | +
|
| 106 | +[[analyzers]] |
| 107 | +name = "javascript" |
| 108 | +enabled = true |
| 109 | +
|
| 110 | +[[analyzers]] |
| 111 | +name = "shell" |
| 112 | +enabled = true |
| 113 | +
|
| 114 | +[[analyzers]] |
| 115 | +name = "docker" |
| 116 | +enabled = true |
| 117 | +`; |
| 118 | +fs.writeFileSync('.deepsource.toml', dsContent); |
| 119 | + |
| 120 | +console.log('Final fixes applied successfully.'); |
0 commit comments