{"record":{"id":"4b418a9b4f49e713","repo":"gastownhall/beads","slug":"agents-file-must-be-a-simple-filename-without-path","errorCode":null,"errorMessage":"agents file must be a simple filename without path separators, got %q","messagePattern":"agents file must be a simple filename without path separators, got %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/config.go","lineNumber":1176,"sourceCode":"\t\tdebug.Logf(\"config: agents.file %q failed validation (%v), using default\", name, err)\n\t\treturn DefaultAgentsFile\n\t}\n\treturn name\n}\n\n// ValidateAgentsFile checks that filename is safe to use as an agents file path.\n// It rejects absolute paths, path separators, names longer than 255 characters,\n// and non-markdown extensions. This is a pure string validation function — I/O\n// checks (e.g. symlink detection) are deferred to the file write layer.\nfunc ValidateAgentsFile(filename string) error {\n\tif filename == \"\" {\n\t\treturn fmt.Errorf(\"agents file name must not be empty\")\n\t}\n\tif len(filename) > 255 {\n\t\treturn fmt.Errorf(\"agents file name exceeds 255 characters\")\n\t}\n\tif strings.ContainsAny(filename, \"/\\\\\") {\n\t\treturn fmt.Errorf(\"agents file must be a simple filename without path separators, got %q\", filename)\n\t}\n\text := strings.ToLower(filepath.Ext(filename))\n\tif ext != \".md\" {\n\t\treturn fmt.Errorf(\"agents file must have .md extension, got %q\", ext)\n\t}\n\treturn nil\n}\n\n// getConfigList retrieves a list-typed configuration value from config.yaml,\n// accepting either the YAML list form (e.g. `types: { custom: [step, wisp] }`)\n// or the legacy comma-separated string form (e.g.\n// `types.custom = \"step,wisp\"`). Entries are trimmed; empty entries are\n// dropped. The dual-form support is required for project-extension\n// types/statuses declared in .beads/config.yaml — see gastownhall/beads#4024.\nfunc getConfigList(key string) []string {\n\tif v == nil {\n\t\tdebug.Logf(\"config: viper not initialized, returning nil for key %q\", key)\n\t\treturn nil","sourceCodeStart":1158,"sourceCodeEnd":1194,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/config/config.go#L1158-L1194","documentation":"ValidateAgentsFile requires a plain filename: any '/' or '\\\\' means the caller passed a path rather than a simple name. This restriction lets the write layer control where agents files live and simplifies symlink/security checks. The offending name is quoted in the message.","triggerScenarios":"Calling ValidateAgentsFile or SafeAgentsFile with values like \"docs/AGENTS.md\", \"/etc/agents.md\", \"sub\\\\dir.md\", or any name containing a path separator.","commonSituations":"A user sets an absolute path in the agents-file config key; a script builds a relative path; Windows-style separators leak in from cross-platform tooling.","solutions":["Pass only the base filename, e.g. \"AGENTS.md\", and let the library place it in the beads directory.","Use filepath.Base on the configured value at the call site before validation.","Update the config key to remove any directory portion."],"exampleFix":"// before\nconfig.SafeAgentsFile(\"docs/AGENTS.md\")\n// after\nconfig.SafeAgentsFile(filepath.Base(\"docs/AGENTS.md\")) // \"AGENTS.md\"","handlingStrategy":"validation","validationCode":"if strings.ContainsAny(name, \"/\\\\\") {\n    return fmt.Errorf(\"pass a bare filename, not a path: %q\", name)\n}\nname = filepath.Base(name) // normalize before calling the library","typeGuard":null,"tryCatchPattern":"if err := config.SafeAgentsFile(name); err != nil {\n    if strings.Contains(err.Error(), \"path separators\") {\n        log.Warn(\"agents file must be a bare name; using base\", \"got\", name)\n        return config.SafeAgentsFile(filepath.Base(name))\n    }\n    return err\n}","preventionTips":["Normalize user-supplied values with filepath.Base before validation.","Document in config comments that the key takes a filename, not a path.","Sanitize Windows separators when importing configs from Windows machines.","Treat path-like values in this setting as a config smell worth warning about."],"tags":["config","validation","filename","path-traversal"],"backgroundTag":"invalid-filename","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}