{"record":{"id":"7f9c1bd128aa56e3","repo":"can1357/oh-my-pi","slug":"security-finding-paths-must-be-repository-relative","errorCode":null,"errorMessage":"Security finding paths must be repository-relative: ${input}","messagePattern":"Security finding paths must be repository-relative: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/security/publication.ts","lineNumber":108,"sourceCode":"\tplan: SecurityScanPlan;\n\tscanId: string;\n\tstore: SecurityStore;\n\tstartedAt: string;\n\tsessionId?: string;\n\toperationId?: string;\n\tonPublished?: (bundle: SecurityScanBundle) => void | Promise<void>;\n}\n\nfunction normalizePublishedPath(input: string): string {\n\tconst normalized = input.replaceAll(\"\\\\\", \"/\").replace(/^\\.\\//, \"\");\n\tconst segments = normalized.split(\"/\");\n\tif (\n\t\t!normalized ||\n\t\tnormalized.startsWith(\"/\") ||\n\t\t/^[a-zA-Z]:\\//.test(normalized) ||\n\t\tsegments.some(segment => segment === \"..\")\n\t) {\n\t\tthrow new Error(`Security finding paths must be repository-relative: ${input}`);\n\t}\n\treturn normalized;\n}\n\nfunction toLocation(\n\tinput: SecurityPublishParams[\"findings\"][number][\"locations\"][number],\n\tplan: SecurityScanPlan,\n): SecurityLocation {\n\tconst normalizedPath = normalizePublishedPath(input.path);\n\tif (!pathMatchesSecurityScope(normalizedPath, plan.target.includePaths, plan.target.excludePaths)) {\n\t\tthrow new Error(`Security finding path is outside the immutable scan scope: ${input.path}`);\n\t}\n\tconst location: SecurityLocation = {\n\t\tpath: normalizedPath,\n\t\tstartLine: input.start_line,\n\t};\n\tif (input.end_line !== undefined) location.endLine = input.end_line;\n\tif (input.start_column !== undefined) location.startColumn = input.start_column;","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/security/publication.ts#L90-L126","documentation":"normalizePublishedPath validates every finding location path before persisting a security publication. A path must be non-empty, repository-relative (no leading '/', no Windows drive prefix, no '..' segments). Absolute or escaping paths would break the repository-relative contract of stored findings and could point outside the project.","triggerScenarios":"Publishing findings (SecurityPublishTool execute -> toLocation -> normalizePublishedPath) where input.path is absolute like '/src/app.ts', a Windows path 'C:/src/app.ts', contains '..' segments, or is empty/whitespace.","commonSituations":"LLM-generated findings carrying absolute paths read from the filesystem, mixing Windows-style scan output into a POSIX publication, or constructing location paths by concatenating the repo root with the relative path.","solutions":["Make the path relative to the canonical repository root before publishing (strip the root prefix with path.relative)","Remove any '..' traversal segments by resolving and re-relativizing the path","Reject/fix empty or whitespace-only path strings at the finding source","Normalize Windows drive-letter paths to the in-repo relative form"],"exampleFix":"// before\nlocations: [{ path: \"/home/me/project/src/app.ts\", start_line: 10 }]\n// after\nlocations: [{ path: path.relative(repoRoot, \"/home/me/project/src/app.ts\"), start_line: 10 }] // \"src/app.ts\"","handlingStrategy":"validation","validationCode":"if (!p || p.startsWith(\"/\") || /^[a-zA-Z]:\\//.test(p) || p.split(\"/\").includes(\"..\")) throw new Error(\"bad path\");","typeGuard":"null","tryCatchPattern":"try { await publish(params); } catch (e) { if (String(e.message).includes(\"repository-relative\")) { /* relativize and retry once */ } else throw e; }","preventionTips":["Store repo-relative paths only","path.relative() from repo root at creation time"],"tags":["validation","path-handling","security"],"backgroundTag":"invalid-path-format","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}