{"record":{"id":"0e54f4e44c0d3773","repo":"ruvnet/ruflo","slug":"localsingleentrypagerank-sourceindex-src-out-o","errorCode":null,"errorMessage":"localSingleEntryPageRank: sourceIndex ${src} out of range [0, ${n})","messagePattern":"localSingleEntryPageRank: sourceIndex (.+?) out of range \\[0, (.+?)\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"plugins/ruflo-neural-trader/src/signed-attribution.ts","lineNumber":219,"sourceCode":" * The math: standard personalized PageRank with the personalization vector\n * concentrated entirely on the source node. Forward-push semantics in the\n * limit, plain power iteration on a small in-memory graph in practice.\n * Seeded so that two runs with the same graph + same seed return byte-\n * identical ordering (asserted by the Phase 6 smoke's reproducibility\n * check).\n */\nexport function localSingleEntryPageRank(\n  graph: AttributionGraph,\n  opts: PageRankOptions,\n): PageRankResult {\n  const n = graph.nodes.length;\n  if (n === 0) return { scores: [], iterations: 0 };\n  const damping = opts.damping ?? 0.85;\n  const maxIter = opts.maxIterations ?? 100;\n  const tol = opts.tolerance ?? 1e-8;\n  const src = opts.sourceIndex;\n  if (src < 0 || src >= n) {\n    throw new Error(\n      `localSingleEntryPageRank: sourceIndex ${src} out of range [0, ${n})`,\n    );\n  }\n\n  // Personalization vector concentrated on src.\n  const personalization = new Float64Array(n);\n  personalization[src] = 1;\n\n  // Initialize: seeded deterministic noise then re-normalize so the start\n  // vector still sums to 1. The seed controls the initialization only —\n  // PageRank converges to the same stationary distribution regardless, but\n  // the iteration *order* and the path through the state space depend on\n  // the seed when ties are present. This is what the smoke asserts.\n  let rng = mulberry32(opts.seed);\n  let r = new Float64Array(n);\n  let sum = 0;\n  for (let i = 0; i < n; i++) {\n    // Small positive noise so we don't divide by zero on degenerate graphs.","sourceCodeStart":201,"sourceCodeEnd":237,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/plugins/ruflo-neural-trader/src/signed-attribution.ts#L201-L237","documentation":"This is the second, defense-in-depth check in getSessionPath(): after the strict charset validation passes, it path.resolve()s both the assembled session file path and the session directory and requires the file path to start with the directory plus a path separator. With the regex already forbidding '/', '\\', and '..', it should be unreachable through the sessionId alone; in practice it fires when the directory itself resolves somewhere unexpected, e.g. process.cwd() contains a symlink so resolvedDir differs from the joined prefix.","triggerScenarios":"MCP server started in a symlinked directory (e.g. /tmp -> /private/tmp on macOS) where path.resolve(sessionPath) and path.resolve(sessionDir) normalize through different symlink chains; DEFAULT_SESSION_DIR (\".claude-flow/sessions\") being itself a symlink pointing elsewhere; exotic cwd states (deleted working directory).","commonSituations":"macOS /tmp symlink issues when running the server from a temp checkout; deployment layouts where the project dir is a symlink (current -> releases/2026-08-18) and different code paths resolve it differently.","solutions":["Start the MCP server from the real (non-symlinked) physical path: cd $(readlink -f .) before launching","Replace the .claude-flow/sessions symlink with a real directory (or bind mount)","Regenerate sessions under the corrected directory so old and new paths agree","If it persists, log path.resolve(cwd) inside getSessionPath to see which prefix mismatch triggers it"],"exampleFix":"# before: server started via a symlinked path\nln -s /srv/releases/2026-08-18 /srv/current\n(cd /srv/current && start-mcp-server)  # session ops may throw [1126]\n\n# after: launch from the physical path\ncd /srv/releases/2026-08-18 && start-mcp-server","handlingStrategy":"validation","validationCode":"import { resolve, join } from 'path';\nfunction sessionPathIsContained(sessionId: string, cwd = process.cwd()): boolean {\n  const dir = resolve(cwd, '.claude-flow/sessions');\n  const file = resolve(dir, `${sessionId}.json`);\n  return file.startsWith(dir + sep);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Launch MCP servers from physical paths, not symlinked convenience links","Keep .claude-flow/sessions a real directory, not a symlink","Treat this error as an environment smell (symlinked cwd) rather than bad input — fix the launch path"],"tags":["mcp","session","path-traversal","security","symlink"],"backgroundTag":"path-traversal-blocked","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}