{"record":{"id":"e1742c03a4810e59","repo":"OpenAPITools/openapi-generator","slug":"template-location-must-be-constrained-to-template","errorCode":null,"errorMessage":"Template location must be constrained to template directory.","messagePattern":"Template location must be constrained to template directory\\.","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/openapi-generator/src/main/java/org/openapitools/codegen/TemplateManager.java","lineNumber":70,"sourceCode":"            TemplatePathLocator[] templateLoaders) {\n        this.options = options;\n        this.engineAdapter = engineAdapter;\n        this.templateLoaders = templateLoaders;\n    }\n\n    private String getFullTemplateFile(String name) {\n        String template = Arrays.stream(this.templateLoaders)\n                .map(i -> i.getFullTemplatePath(name))\n                .filter(Objects::nonNull)\n                .findFirst()\n                .orElse(\"\");\n\n        if (StringUtils.isEmpty(template)) {\n            throw new TemplateNotFoundException(name);\n        }\n\n        if (name == null || name.contains(\"..\")) {\n            throw new IllegalArgumentException(\"Template location must be constrained to template directory.\");\n        }\n\n        return template;\n    }\n\n    /**\n     * returns the template content by name\n     *\n     * @param name the template name (e.g. model.mustache)\n     * @return the contents of that template\n     */\n    @Override\n    public String getFullTemplateContents(String name) {\n        String fullPath = getFullTemplateFile(name);\n        return templateContentCache.computeIfAbsent(fullPath, this::readTemplate);\n    }\n\n    /**","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/OpenAPITools/openapi-generator/blob/fcec517be3cf5b7964296bcba25fbc97541484e7/modules/openapi-generator/src/main/java/org/openapitools/codegen/TemplateManager.java#L52-L88","documentation":"Guard inside TemplateManager.getFullTemplateFile(): after resolving the template, it rejects a name that is null or contains '..' to block path traversal outside the template directory. Note the ordering quirk: resolution happens first, so a null name actually throws TemplateNotFoundException earlier (StringUtils.isEmpty(null)); in practice this guard fires for names containing '..'. It is a security check, not a 'file missing' check.","triggerScenarios":"A template name like '../templates/model.mustache' or 'foo/../../model.mustache' passed through generator settings or a custom generator that builds names from user input; template name concatenated with a path that itself contains '..'.","commonSituations":"Custom generators constructing template paths from additionalProperties values; CI configurations passing relative paths with '..' as template names; attempts to reuse a template from another generator's directory via traversal.","solutions":["Remove '..' segments from the template name — reference templates by their name relative to a configured template directory or classpath root.","Copy the needed template into your template directory and reference it by plain filename.","Sanitize any user-supplied template name before passing it to generation APIs (reject/normalize '..')."],"exampleFix":"// before\nString name = \"../JavaSpring/model.mustache\";\n// after\nString name = \"model.mustache\"; // put the file in the -t template dir instead","handlingStrategy":"validation","validationCode":"boolean safeTemplateName(String name) {\n    return name != null && !name.contains(\"..\") && !name.startsWith(\"/\");\n}","typeGuard":null,"tryCatchPattern":"Catch IllegalArgumentException and reject the input at your API boundary; treat as untrusted input, log the attempt.","preventionTips":["Normalize template names (Path.normalize + startsWith check) before use.","Never build template names from raw user input.","Treat any '..' in a template name as a security event, not a typo."],"tags":["openapi","template","path-traversal","security","illegal-argument"],"backgroundTag":"path-traversal-blocked","analyzedSha":"fcec517be3cf5b7964296bcba25fbc97541484e7","analyzedAt":"2026-08-22T11:13:11.613Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}