{"record":{"id":"0a371be2f319993d","repo":"larksuite/cli","slug":"s-q-is-inside-s-which-is-protected-by-the-buil","errorCode":null,"errorMessage":"%s %q is inside %s, which is protected by the built-in denylist","messagePattern":"(.+?) %q is inside (.+?), which is protected by the built-in denylist","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/vfs/localfileio/policy.go","lineNumber":293,"sourceCode":"\troots := slices.Concat(denyRoots(), configDirDenyRoots(cwd))\n\tfor _, e := range roots {\n\t\tif matchResolved(resolved, e) || isUnderDir(foldCase(absLiteral), foldCase(e.literal)) {\n\t\t\treturn denyError(flagName, raw, e.label)\n\t\t}\n\t}\n\t// Name comparison alone cannot decide containment: case-insensitive and\n\t// case-folding filesystems (APFS folds U+017F to \"s\", so \".ſſh\" opens\n\t// \"~/.ssh\"), Unicode normalization, and Windows short names all give the\n\t// same directory several spellings. Ask the kernel instead — file identity\n\t// has exactly one answer per directory.\n\tif label, ok := matchByFileIdentity(resolved, roots); ok {\n\t\treturn denyError(flagName, raw, label)\n\t}\n\treturn nil\n}\n\nfunc denyError(flagName, raw, label string) error {\n\treturn fmt.Errorf(\"%s %q is inside %s, which is protected by the built-in denylist\", flagName, raw, label)\n}\n\n// matchByFileIdentity walks resolved upwards and reports the first root that\n// is the very same directory as one of the ancestors, compared by device and\n// inode rather than by name. Missing ancestors are skipped: a target that does\n// not exist yet is decided by its nearest existing parent.\nfunc matchByFileIdentity(resolved string, roots []policyEntry) (string, bool) {\n\tp := resolved\n\tfor {\n\t\tif fi, err := vfs.Lstat(p); err == nil {\n\t\t\tfor _, e := range roots {\n\t\t\t\tif e.info != nil && os.SameFile(fi, e.info) {\n\t\t\t\t\treturn e.label, true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tparent := filepath.Dir(p)\n\t\tif parent == p {","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/vfs/localfileio/policy.go#L275-L311","documentation":"The built-in denylist protects sensitive locations (e.g. config, credentials, CLI-owned state). checkDeny compares the resolved path's ancestors against deny roots — by device/inode identity where possible — and denyError reports the offending flag name, raw value, and protected label.","triggerScenarios":"Passing a file/directory inside a denylisted root to a flag that accepts local paths (flagName in the message), e.g. --config pointing under the CLI's own state directory or another protected root; matched even via symlink/alias spellings because matching resolves ancestors.","commonSituations":"Trying to overwrite CLI credentials/config through a data-upload flag, or a symlink pointing into a protected directory that the denylist catches.","solutions":["Move the file outside the protected root and pass the new path","Check the label in the message to learn which protected root was matched and pick a different working directory","If the operation is legitimate CLI-state management, use the dedicated CLI command for that state instead of a generic file flag"],"exampleFix":"// before\n--config /home/me/.lark-cli/config.yaml --upload /home/me/.lark-cli/cache.bin\n// after\n--config /home/me/.lark-cli/config.yaml --upload /tmp/exports/cache.bin","handlingStrategy":"validation","validationCode":"// resolve symlinks and check the target is not under a protected root before invoking\nresolved, _ := filepath.EvalSymlinks(path)\nif strings.HasPrefix(resolved, protectedRoot) {\n\t// pick another location\n}","typeGuard":null,"tryCatchPattern":"if strings.Contains(err.Error(), \"protected by the built-in denylist\") {\n\t// move the file out of the protected root and retry with the new path\n}","preventionTips":["Keep working files out of CLI state/config directories and other known protected roots","Remember denylist matching follows symlinks by identity — pointing a symlink into a protected root will be caught","Use a dedicated scratch/export directory for files passed to file flags"],"tags":["filesystem","security","denylist"],"backgroundTag":"protected-path-denylist","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}