{"record":{"id":"25478640a984512c","repo":"projectdiscovery/nuclei","slug":"path-v-is-outside-nuclei-template-directory-and","errorCode":null,"errorMessage":"path %v is outside nuclei-template directory and -allow-local-file-access is not enabled","messagePattern":"path (.+?) is outside nuclei-template directory and -allow-local-file-access is not enabled","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/js/libs/krbforge/krbforge.go","lineNumber":236,"sourceCode":"\n\t\treturn normalized, nil\n\t}\n\n\tnormalized := outputFile\n\tif !filepath.IsAbs(normalized) {\n\t\tnormalized = filepath.Join(config.DefaultConfig.GetTemplateDir(), normalized)\n\t}\n\n\tnormalized, err := filepath.Abs(normalized)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"normalize output file %q: %w\", outputFile, err)\n\t}\n\n\tif filepathutil.IsPathWithinDirectory(normalized, config.DefaultConfig.GetTemplateDir()) {\n\t\treturn normalized, nil\n\t}\n\n\treturn \"\", fmt.Errorf(\"path %v is outside nuclei-template directory and -allow-local-file-access is not enabled\", outputFile)\n}\n\nfunc exportTicketRequest(vm *goja.Runtime, value goja.Value) (TicketRequest, error) {\n\tvar req TicketRequest\n\tif err := vm.ExportTo(value, &req); err != nil {\n\t\treturn req, fmt.Errorf(\"invalid TicketRequest: %w\", err)\n\t}\n\treturn req, nil\n}\n\nfunc exportOutputFile(value goja.Value) (string, error) {\n\tif goja.IsUndefined(value) || goja.IsNull(value) {\n\t\treturn \"\", nil\n\t}\n\toutputFile, ok := value.Export().(string)\n\tif !ok {\n\t\treturn \"\", fmt.Errorf(\"outputFile must be a string\")\n\t}","sourceCodeStart":218,"sourceCodeEnd":254,"githubUrl":"https://github.com/projectdiscovery/nuclei/blob/265b3a3dec374741614e342f813c10f8b38d2bb7/pkg/js/libs/krbforge/krbforge.go#L218-L254","documentation":"Thrown by krbforge's normalizeOutputFile when the ticket OutputFile resolves to a path outside the nuclei templates directory while -allow-local-file-access is not enabled. Nuclei sandboxes file writes from JavaScript templates: relative paths are joined onto the template directory, absolute paths must already lie inside it. This guard prevents a template from persisting a forged-ticket ccache to arbitrary locations on the host.","triggerScenarios":"Calling krb.CreateSilverTicket(req, '/tmp/silver.ccache') or krb.CreateGoldenTicket({..., output_file: '/etc/evil'}) without -allow-local-file-access; also a relative path that escapes the sandbox via '../..' segments, since normalization joins it onto GetTemplateDir() and the IsPathWithinDirectory check then fails.","commonSituations":"Template authors copying the doc example verbatim (it uses '/tmp/silver.ccache'); running nuclei with default flags where the sandbox is enforced; LFA enabled on one machine but not in CI, so the same template fails only there.","solutions":["Omit the outputFile argument (or pass '-' / leave output_file empty) so no file is written and the ticket is returned in-memory as ticket_hex/session_key_hex","Pass a bare relative filename (e.g. 'silver.ccache') so it normalizes inside the nuclei templates directory","If writing outside the sandbox is intentional, run nuclei with -allow-local-file-access (note: with LFA on, relative paths are placed in os.TempDir(), not CWD)","Have the template copy the returned hex to the desired sink instead of writing a ccache file"],"exampleFix":"// before\nconst t = krb.CreateSilverTicket(req, '/tmp/silver.ccache'); // throws: outside sandbox\n\n// after (no file written)\nconst t = krb.CreateSilverTicket(req);\nlog(t.ticket_hex);\n\n// after (sandboxed file)\nconst t = krb.CreateSilverTicket(req, 'silver.ccache');","handlingStrategy":"validation","validationCode":"// decide up front where the ccache may go\nconst lfaEnabled = templateallowsLocalFileAccess; // from scan config\nlet outFile;\nif (!lfaEnabled) {\n  outFile = 'ticket.ccache';           // bare name -> inside nuclei templates dir\n} else if (lfaEnabled === true) {\n  outFile = '/tmp/ticket.ccache';      // allowed with -allow-local-file-access\n}\n// or skip file output entirely: pass no second argument / no output_file","typeGuard":null,"tryCatchPattern":"try {\n  const t = krb.CreateSilverTicket(req, outFile);\n} catch (e) {\n  if (String(e).includes('outside nuclei-template directory')) {\n    // sandbox denial: fall back to in-memory ticket only\n    return krb.CreateSilverTicket(req);\n  }\n  throw e;\n}","preventionTips":["Default to no OutputFile: the ticket_hex/session_key_hex return values usually suffice","Never hardcode absolute paths like /tmp/... in templates meant to run under default flags","Document in the template metadata that -allow-local-file-access is required if a ccache write is intentional"],"tags":["filesystem","sandbox","security","krbforge","javascript","kerberos"],"backgroundTag":null,"analyzedSha":"265b3a3dec374741614e342f813c10f8b38d2bb7","analyzedAt":"2026-08-15T20:05:51.855Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}