fix(security): resolve 5 CodeQL alerts, document FPs, harden deploy skills

Real fixes (alerts auto-close on next scan):
- agentSkills/generator: escape backslash before double-quote in YAML
  frontmatter so a trailing backslash can't escape the closing quote
  (js/incomplete-sanitization #297/#298)
- usage: replace /\s*\(RESTRICTED\)\s*$/ with a non-backtracking literal +
  trim (js/polynomial-redos #275)
- i18n/request: skip __proto__/constructor/prototype in deepMergeFallback
  (js/prototype-pollution-utility #274)
- scripts/ad-hoc/nvidia diag: log key presence only, never key chars
  (js/clear-text-logging #273)
- tests: regression coverage for all three production fixes (YAML round-trip,
  proto-pollution guard, RESTRICTED strip + ReDoS timing)

Docs:
- ARCHITECTURE.md: executor count 45->55, OAuth modules 15->16, agy.ts in list

Deploy skills (deploy-vps-*-cc/-cx/-ag): add --legacy-peer-deps (npm v11
peer-dep resolver crashes on the omniroute tree) and replace the
"; pm2 start" that masked a failed install with a proper && chain.
This commit is contained in:
diegosouzapw
2026-06-01 19:36:21 -03:00
parent 2a8954663c
commit 8f0615fd04
13 changed files with 94 additions and 16 deletions

View File

@@ -15,6 +15,8 @@ export function deepMergeFallback(
source: Record<string, unknown>
): Record<string, unknown> {
for (const [key, sourceValue] of Object.entries(source)) {
// Guard against prototype pollution from a crafted locale message tree.
if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
const targetValue = target[key];
if (
sourceValue !== null &&

View File

@@ -48,9 +48,13 @@ function serializeFrontmatter(fm: { name: string; description: string }): string
// Use block scalar for description if it contains colons, quotes, or newlines
const needsQuote = (v: string): boolean => /[:\n"']/.test(v) || v.startsWith(" ");
const nameStr = needsQuote(fm.name) ? `"${fm.name.replace(/"/g, '\\"')}"` : fm.name;
// Escape order matters for double-quoted YAML scalars: backslash FIRST (so the
// escapes we add below are not themselves re-escaped), then the double-quote,
// then collapse real newlines into the \n escape sequence.
const escDq = (v: string): string => v.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
const nameStr = needsQuote(fm.name) ? `"${escDq(fm.name)}"` : fm.name;
const descStr = needsQuote(fm.description)
? `"${fm.description.replace(/"/g, '\\"').replace(/\n/g, "\\n")}"`
? `"${escDq(fm.description).replace(/\n/g, "\\n")}"`
: fm.description;
return `---\nname: ${nameStr}\ndescription: ${descStr}\n---\n`;
@@ -363,3 +367,7 @@ export async function generateAgentSkills(opts: GeneratorOptions): Promise<Gener
return report;
}
// Exported for unit testing only — keep the internal helpers reachable without
// widening the public surface.
export const __testing = { serializeFrontmatter };