{"record":{"id":"d9f896ff6fe493e4","repo":"OtterMind/Chat2DB","slug":"unsafe-what-name-name","errorCode":null,"errorMessage":"Unsafe {what} name: {name}","messagePattern":"Unsafe (.+?) name: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"chat2db-community-server/chat2db-community-plugins/chat2db-community-sqlite/src/main/java/ai/chat2db/plugin/sqlite/SqliteSqlGuards.java","lineNumber":53,"sourceCode":"            \"AUTOINCREMENT\", \"CHECK\", \"COLLATE\", \"CONSTRAINT\", \"DEFAULT\", \"GENERATED\",\n            \"NOT\", \"NULL\", \"PRIMARY\", \"REFERENCES\", \"UNIQUE\");\n    private static final Set<String> STATEMENT_KEYWORDS = Set.of(\n            \"ALTER\", \"ATTACH\", \"CREATE\", \"DELETE\", \"DETACH\", \"DROP\", \"INSERT\", \"PRAGMA\",\n            \"REINDEX\", \"REPLACE\", \"SELECT\", \"UPDATE\", \"VACUUM\");\n\n    private SqliteSqlGuards() {\n    }\n\n    /**\n     * Validates a name embedded into a keyword position (collation, charset) against a\n     * conservative allow-list. Returns the name unchanged when safe; throws otherwise\n     * (fail closed).\n     *\n     * @throws IllegalArgumentException if the name contains unexpected characters\n     */\n    public static String requireSafeName(String name, String what) {\n        if (name == null || !SAFE_NAME.matcher(name).matches()) {\n            throw new IllegalArgumentException(\"Unsafe \" + what + \" name: \" + name);\n        }\n        return name;\n    }\n\n    /**\n     * Validates a free-text column type name before it is embedded into generated DDL.\n     * Returns the type name unchanged when it matches a conservative allow-list;\n     * throws otherwise (fail closed).\n     *\n     * @throws IllegalArgumentException if the type name contains unexpected characters\n     */\n    public static String requireSafeTypeName(String typeName) {\n        String expression = StringUtils.trimToNull(typeName);\n        if (expression == null) {\n            return null;\n        }\n        scanExpression(expression, true, \"column type\");\n        return expression;","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/OtterMind/Chat2DB/blob/5ee1e990e73fbcae1969dc554be254fedb3ab888/chat2db-community-server/chat2db-community-plugins/chat2db-community-sqlite/src/main/java/ai/chat2db/plugin/sqlite/SqliteSqlGuards.java#L35-L71","documentation":"Thrown by SqliteSqlGuards.requireSafeName when a name destined for a non-escapable keyword position (collation/charset) is null or does not fully match the allow-list regex [A-Za-z0-9_]+. Because these names cannot be quoted/escaped, anything outside ASCII alphanumerics and underscore is rejected to prevent DDL injection (fail closed).","triggerScenarios":"Passing a collation or charset name containing spaces, dots, parentheses, quotes, or any non-[A-Za-z0-9_] character (e.g. 'NOCASE' is fine, but 'BINARY' with a trailing space, or a localized/custom collation like 'zh_phonebook' is fine whereas 'zh phonebook' is not). A null name also triggers it.","commonSituations":"User-typed or metadata-supplied collation with whitespace/punctuation; copy-paste introducing a stray character; a custom collation whose name contains a hyphen.","solutions":["Trim and restrict the name to ASCII letters, digits, and underscore.","Pre-validate with the same [A-Za-z0-9_]+ pattern before calling the DDL builder.","If a genuinely exotic collation name is required, register/handle it through a quoted path rather than the keyword-position path."],"exampleFix":"// before\ncolumn.setCollationName(\"zh phonebook\"); // space not allowed\n\n// after\ncolumn.setCollationName(\"zh_phonebook\");","handlingStrategy":"validation","validationCode":"// Pre-check the same allow-list the guard uses\nprivate static final java.util.regex.Pattern SAFE = java.util.regex.Pattern.compile(\"[A-Za-z0-9_]+\");\nif (name != null && !SAFE.matcher(name).matches()) {\n    throw new IllegalArgumentException(\"Name fails allow-list: \" + name);\n}","typeGuard":"static boolean isSafeSqliteName(String name) {\n    return name != null && name.matches(\"[A-Za-z0-9_]+\");\n}","tryCatchPattern":null,"preventionTips":["Trim and restrict collation/charset names to ASCII alphanumerics and underscore.","Validate at the boundary with the same regex before DDL generation.","Treat null names as 'omit the clause' rather than forwarding them."],"tags":["sqlite","ddl","validation","security","collation"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}