{"record":{"id":"7482c4e3d24abc6a","repo":"hibernate/hibernate-orm","slug":"function-functionname-tolowercase-is-n","errorCode":null,"errorMessage":"Function [\" + functionName.toLowerCase() + \"] is not allowed in safe mode","messagePattern":"Function \\[\" \\+ functionName\\.toLowerCase\\(\\) \\+ \"\\] is not allowed in safe mode","errorType":"exception","errorClass":"SemanticException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java","lineNumber":1416,"sourceCode":"\t\t\t\t\tif ( !(parameter instanceof ValueBindJpaCriteriaParameter) ) {\n\t\t\t\t\t\tparameterExpressions.add( parameter );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tyield unmodifiableSet( parameterExpressions );\n\t\t\t}\n\t\t};\n\t}\n\n\t/**\n\t * Throws a {@link SemanticException} if safe mode is enabled and the function is not allowed.\n\t *\n\t * @param safeModeEnabled whether safe mode is enabled\n\t * @param functionName the name of the function to validate (will be converted to lowercase)\n\t * @throws SemanticException if safe mode is enabled\n\t */\n\tpublic static void failIfSafeModeEnabled(boolean safeModeEnabled, String functionName, @Nullable String queryString) {\n\t\tif ( safeModeEnabled ) {\n\t\t\tthrow new SemanticException( \"Function [\" + functionName.toLowerCase() + \"] is not allowed in safe mode\", queryString );\n\t\t}\n\t}\n}\n","sourceCodeStart":1398,"sourceCodeEnd":1420,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java#L1398-L1420","documentation":"Thrown as SemanticException by SqmUtil.failIfSafeModeEnabled when safe mode is on and a disallowed function is used in HQL or Criteria. Safe mode (hibernate.query.safe_mode_enabled, QuerySettings.SAFE_MODE_ENABLED, default false, incubating since Hibernate 8.0) hardens queries against untrusted input: only explicitly registered/contributed functions are permitted, and escape hatches like the 'sql()' and 'column()' fragments in Criteria are blocked outright. The message lowercases the function name, e.g. 'Function [sql] is not allowed in safe mode'.","triggerScenarios":"Setting hibernate.query.safe_mode_enabled=true (e.g. to accept HQL from users) and then executing queries that call unregistered functions; Criteria code using cb.function(\"sql\", ...) or the sql()/column() escape APIs (SqmCriteriaNodeBuilder calls this check with name \"sql\" and \"column\"); custom dialect functions used in HQL that were never contributed through a FunctionContributor.","commonSituations":"Enabling safe mode for a query-builder/reporting feature that stores user-written HQL; upgrading to Hibernate 8 where the flag exists and ops turns it on globally; applications relying on ad-hoc SQL fragments inside criteria; multi-tenant products that must not let tenants call arbitrary DB functions.","solutions":["Contribute the function explicitly: implement org.hibernate.boot.model.FunctionContributor and register the function via FunctionContributions.getFunctionRegistry()","Replace the blocked call with a registered dialect function or a normal HQL/Criteria expression","If all queries are trusted, disable safe mode (remove hibernate.query.safe_mode_enabled=true)","Keep user-supplied HQL in a separate SessionFactory configured with safe mode, and route internal trusted queries through a normal one"],"exampleFix":"// before: hibernate.query.safe_mode_enabled=true and HQL 'select reverse(p.name) from Person p' -> SemanticException\n// after: contribute the function so safe mode accepts it\npublic class MyFunctionContributor implements FunctionContributor {\n    @Override\n    public void contributeFunctions(FunctionContributions contributions) {\n        contributions.getFunctionRegistry().registerPattern(\n                \"reverse\", \"reverse(?1)\",\n                contributions.getTypeConfiguration().getBasicTypeRegistry().resolve(String.class)\n        );\n    }\n}","handlingStrategy":"try-catch","validationCode":"// before executing user-supplied HQL with safe mode on, screen the function names it may call\nstatic final Set<String> ALLOWED = Set.of(\"lower\", \"upper\", \"length\", \"substring\", \"concat\");\nstatic boolean onlyAllowedFunctions(String hql) {\n    Matcher m = Pattern.compile(\"([A-Za-z_][A-Za-z0-9_]*)\\\\s*\\\\(\").matcher(hql);\n    while (m.find()) {\n        if (!ALLOWED.contains(m.group(1).toLowerCase(Locale.ROOT))) return false;\n    }\n    return true;\n}","typeGuard":null,"tryCatchPattern":"try {\n    return session.createSelectionQuery(userHql, Object[].class).getResultList();\n} catch (SemanticException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"safe mode\")) {\n        // unregistered function: reject the query, do not silently disable safe mode\n        throw new SecurityException(\"Query uses a function not allowed in safe mode\", e);\n    }\n    throw e;\n}","preventionTips":["Register every custom function via FunctionContributor so safe mode accepts it","Never use cb.sql()/column() fragments in code paths where safe mode may be enabled","Keep two SessionFactories: one hardened (safe mode) for user queries, one normal for internal queries","Document the allowed function list for query authors"],"tags":["hibernate","safe-mode","hql-functions","security","criteria"],"backgroundTag":"unknown-sql-function","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}