#!/usr/bin/env node /* * Guard against putting secrets directly in executable Gitea workflow text. * Gitea/act_runner renders `run:` scripts before masking is effective, so * `${{ secrets.* }}` must stay out of shell bodies and action `with:` inputs. * Narrow step `env:` is the least-bad transport here because the printed shell * body can reference only variable names. */ const fs = require("fs"); const path = require("path"); const root = path.resolve(__dirname, "../.."); const workflowDir = path.join(root, ".gitea", "workflows"); const violations = []; const routeViolations = []; const secretExprPattern = /\$\{\{\s*secrets\./; for (const fileName of fs.readdirSync(workflowDir).sort()) { if (!fileName.endsWith(".yml") && !fileName.endsWith(".yaml")) { continue; } const filePath = path.join(workflowDir, fileName); const content = fs.readFileSync(filePath, "utf8"); const lines = content.split(/\r?\n/); let block = null; if (content.includes("TELEGRAM_ALERT_CHAT_ID")) { routeViolations.push(`${filePath}: legacy TELEGRAM_ALERT_CHAT_ID is not allowed; use SRE_GROUP_CHAT_ID`); } if (content.includes("TELEGRAM_CHAT_ID")) { routeViolations.push(`${filePath}: legacy TELEGRAM_CHAT_ID is not allowed for alert routing; use SRE_GROUP_CHAT_ID`); } let lineOffset = 0; lines.forEach((line, index) => { if ( line.includes("api.telegram.org/bot") && !content.slice(Math.max(0, lineOffset - 700), lineOffset + line.length + 1200).includes("SRE_GROUP_CHAT_ID") ) { routeViolations.push(`${filePath}:${index + 1}: direct Telegram fallback must target SRE_GROUP_CHAT_ID`); } lineOffset += line.length + 1; }); lines.forEach((line, index) => { const indent = line.match(/^\s*/)[0].length; const trimmed = line.trim(); if (block && trimmed && indent <= block.indent) { block = null; } if ((trimmed.startsWith("run:") || trimmed.startsWith("with:")) && secretExprPattern.test(line)) { const inlineSection = trimmed.startsWith("with:") ? "with" : "run"; violations.push(`${filePath}:${index + 1}:${inlineSection}`); } const blockMatch = line.match(/^(\s*)(env|with|run):\s*(?:[>|].*)?$/); if (blockMatch) { block = { indent: blockMatch[1].length, section: blockMatch[2], }; return; } if (block && block.section !== "env" && secretExprPattern.test(line)) { violations.push(`${filePath}:${index + 1}:${block.section}`); } }); } if (violations.length > 0) { console.error("Gitea workflow exposes secrets through run/with text:"); for (const violation of violations) { console.error(` - ${violation}`); } process.exit(1); } if (routeViolations.length > 0) { console.error("Gitea workflow Telegram route must converge on AwoooI SRE war room:"); for (const violation of routeViolations) { console.error(` - ${violation}`); } process.exit(1); } console.log("no Gitea run/with secrets or legacy Telegram routes");