{"record":{"id":"1ced0817d5e9930c","repo":"abhigyanpatwari/GitNexus","slug":"watch-ownerid-is-not-a-safe-filename-component","errorCode":null,"errorMessage":"watch ownerId is not a safe filename component","messagePattern":"watch ownerId is not a safe filename component","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/auto-sync/starter.ts","lineNumber":528,"sourceCode":"}\n\nasync function readStatusFile(statusPath: string): Promise<WatchStatusRecord | undefined> {\n  try {\n    const parsed = JSON.parse(await fs.readFile(statusPath, 'utf-8')) as WatchStatusRecord;\n    return parsed && typeof parsed === 'object' ? parsed : undefined;\n  } catch (err: unknown) {\n    if ((err as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n    return {\n      state: 'error',\n      message: `unable to read status file: ${(err as Error).message}`,\n      updatedAt: new Date().toISOString(),\n    };\n  }\n}\n\nfunction stopRequestPath(paths: AutoSyncWatchPaths, ownerId: string): string {\n  if (!isSafeWatchOwnerId(ownerId)) {\n    throw new Error('watch ownerId is not a safe filename component');\n  }\n  return path.join(path.dirname(paths.pidPath), `watch.stop.${ownerId}.json`);\n}\n\nasync function readStopRequest(filePath: string): Promise<WatchStopRequestRecord | undefined> {\n  try {\n    const parsed = JSON.parse(await fs.readFile(filePath, 'utf-8')) as WatchStopRequestRecord;\n    if (\n      parsed &&\n      typeof parsed === 'object' &&\n      Number.isInteger(parsed.pid) &&\n      parsed.pid > 0 &&\n      typeof parsed.ownerId === 'string' &&\n      parsed.ownerId &&\n      typeof parsed.processStartTime === 'string' &&\n      parsed.processStartTime &&\n      typeof parsed.requestedAt === 'string' &&\n      parsed.requestedAt","sourceCodeStart":510,"sourceCodeEnd":546,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/0d1aed942f0e8b5d3bac27519fff441aceea722d/gitnexus/src/core/auto-sync/starter.ts#L510-L546","documentation":"stopRequestPath builds a filesystem path containing the ownerId (watch.stop.<ownerId>.json). Because ownerId becomes a filename component, it must pass isSafeWatchOwnerId; otherwise it could escape the directory (path traversal) or contain separators. An unsafe ownerId is rejected with this error before any file is touched.","triggerScenarios":"Calling stopAutoSyncWatch, request, or cleanupWatchFiles (which call stopRequestPath) with an ownerId containing path separators, '..' , or other characters rejected by isSafeWatchOwnerId.","commonSituations":"Passing a user-supplied or externally deserialized watch id straight into the stop API; an ownerId read from a corrupted/hand-edited state file; forgetting to generate ids via the library's safe generator.","solutions":["Use the ownerId exactly as returned by the watch starter (its generated id is guaranteed safe).","Sanitize or regenerate the ownerId: use only [A-Za-z0-9._-] and reject '/' , '\\\\', and '..'.","Validate the id with isSafeWatchOwnerId before calling the stop API."],"exampleFix":"// before\nawait stopAutoSyncWatch(paths, userInput.id); // e.g. '../evil'\n\n// after\nif (!isSafeWatchOwnerId(userInput.id)) {\n  throw new Error('ignoring unsafe watch ownerId');\n}\nawait stopAutoSyncWatch(paths, userInput.id);","handlingStrategy":"validation","validationCode":"import { isSafeWatchOwnerId } from './auto-sync/starter';\nif (!isSafeWatchOwnerId(ownerId)) {\n  throw new Error('rejecting unsafe watch ownerId');\n}","typeGuard":"const isSafeOwnerId = (id: string): boolean => /^[A-Za-z0-9._-]+$/.test(id) && !id.includes('..');","tryCatchPattern":"try {\n  await stopAutoSyncWatch(paths, ownerId);\n} catch (err) {\n  if (String(err.message).includes('not a safe filename component')) {\n    logger.warn('malformed ownerId, ignoring stop request');\n    return;\n  }\n  throw err;\n}","preventionTips":["Only use ids minted by the watch starter.","Validate any externally supplied id against isSafeWatchOwnerId before filesystem use.","Never interpolate untrusted strings into file paths."],"tags":["validation","path-traversal","filesystem","auto-sync"],"backgroundTag":"path-traversal-blocked","analyzedSha":"0d1aed942f0e8b5d3bac27519fff441aceea722d","analyzedAt":"2026-09-08T00:40:44.970Z","contentChangedAt":"2026-09-08T00:40:44.970Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}