{"record":{"id":"5318c5fdf8c881ae","repo":"ruvnet/ruflo","slug":"dangerous-key-segment-rejected-part","errorCode":null,"errorMessage":"Dangerous key segment rejected: ${part}","messagePattern":"Dangerous key segment rejected: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/mcp-tools/config-tools.ts","lineNumber":109,"sourceCode":"function filterDangerousKeys(obj: Record<string, unknown>): Record<string, unknown> {\n  const filtered: Record<string, unknown> = {};\n  for (const [key, value] of Object.entries(obj)) {\n    if (!DANGEROUS_KEYS.has(key)) {\n      filtered[key] = value;\n    }\n  }\n  return filtered;\n}\n\nfunction setNestedValue(obj: Record<string, unknown>, key: string, value: unknown): void {\n  const MAX_NESTING_DEPTH = 10;\n  const parts = key.split('.');\n  if (parts.length > MAX_NESTING_DEPTH) {\n    throw new Error(`Key exceeds maximum nesting depth of ${MAX_NESTING_DEPTH}`);\n  }\n  for (const part of parts) {\n    if (DANGEROUS_KEYS.has(part)) {\n      throw new Error(`Dangerous key segment rejected: ${part}`);\n    }\n  }\n  let current = obj;\n  for (let i = 0; i < parts.length - 1; i++) {\n    const part = parts[i];\n    if (!(part in current) || typeof current[part] !== 'object') {\n      current[part] = {};\n    }\n    current = current[part] as Record<string, unknown>;\n  }\n  current[parts[parts.length - 1]] = value;\n}\n\nexport const configTools: MCPTool[] = [\n  {\n    name: 'config_get',\n    description: 'Get configuration value Use when native settings.json edits are wrong because the values need to be read by the Ruflo runtime (daemon, MCP server, neural router) — those load via the config_* path, not by re-reading settings.json. For .gitignore / .editorconfig style files, native Edit is fine.',\n    category: 'config',","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/mcp-tools/config-tools.ts#L91-L127","documentation":"Thrown by setNestedValue to block prototype pollution: any segment of a dotted config key that equals '__proto__', 'constructor', or 'prototype' (the DANGEROUS_KEYS set) is rejected. Writing through these keys on a plain object would mutate Object.prototype and is a classic injection vector, so the tool fails closed.","triggerScenarios":"Calling config_set with a key containing a segment exactly named '__proto__', 'constructor', or 'prototype' — e.g. 'a.__proto__.polluted' or 'constructor.prototype.x'. The check iterates all segments before any write occurs.","commonSituations":"User-supplied or LLM-generated key strings routed straight into config_set; a key derived from a filename or identifier that happens to be 'constructor'; security testing/fuzzing that probes for prototype pollution; merging untrusted JSON whose keys become config paths.","solutions":["Rename the offending segment to a safe literal (e.g. 'proto' or 'ctor').","Sanitise user-derived keys by rejecting or mapping the three dangerous names before calling config_set.","Treat this error as intentional fail-closed behaviour — do not catch and retry with the same key.","Audit the upstream source of the key (CLI arg, HTTP body, LLM output) and constrain it to an allowlist."],"exampleFix":"// before\nconfig_set('obj.__proto__.polluted', true)\n// after\nconfig_set('obj.proto.polluted', true)","handlingStrategy":"validation","validationCode":"const DANGEROUS = new Set(['__proto__', 'constructor', 'prototype']);\nfunction assertSafeConfigKey(key) {\n  for (const part of key.split('.')) {\n    if (DANGEROUS.has(part)) throw new Error(`unsafe config key segment: ${part}`);\n  }\n}","typeGuard":"function isSafeConfigKey(key: string): boolean {\n  const dangerous = new Set(['__proto__', 'constructor', 'prototype']);\n  return key.split('.').every((p) => !dangerous.has(p));\n}","tryCatchPattern":null,"preventionTips":["Never route untrusted/LLM-generated strings into config keys unfiltered.","Maintain an allowlist of config keys at the application boundary.","Treat this throw as intentional; do not catch-and-continue with the same key."],"tags":["config","security","prototype-pollution","validation","input-validation"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}