{"record":{"id":"132daa8fa1ae2432","repo":"Tencent/APIJSON","slug":"ck-column-column0-column1-alias-132daa","errorCode":null,"errorMessage":"字符 {ck} 不合法！预编译模式下 @column:\"column0,column1:alias;function0(arg0,arg1,...);function1(...):alias...\" 中所有 arg 都必须是1个不以 _ 开头的单词 或者符合正则表达式 {PATTERN_FUNCTION} 且不包含连续减号 -- ！DISTINCT 必须全大写，且后面必须有且只有 1 个空格！其它情况不允许空格！","messagePattern":"字符 (.+?) 不合法！预编译模式下 @column:\"column0,column1:alias;function0\\(arg0,arg1,\\.\\.\\.\\);function1\\(\\.\\.\\.\\):alias\\.\\.\\.\" 中所有 arg 都必须是1个不以 _ 开头的单词 或者符合正则表达式 (.+?) 且不包含连续减号 -- ！DISTINCT 必须全大写，且后面必须有且只有 1 个空格！其它情况不允许空格！","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java","lineNumber":2738,"sourceCode":"\t\t\t\t\telse {\n\t\t\t\t\t\torigin = ck;\n\t\t\t\t\t\talias = null;\n\t\t\t\t\t\tif (allowAlias) {\n\t\t\t\t\t\t\tint index = isColumn ? ck.lastIndexOf(\":\") : -1; //StringUtil.split返回数组中，子项不会有null\n\t\t\t\t\t\t\torigin = index < 0 ? ck : ck.substring(0, index); //获取 : 之前的\n\t\t\t\t\t\t\talias = index < 0 ? null : ck.substring(index + 1);\n\t\t\t\t\t\t\tif (isPrepared()) {\n\t\t\t\t\t\t\t\tif (isColumn) {\n\t\t\t\t\t\t\t\t\tif (StringUtil.isName(origin) == false || (alias != null && StringUtil.isName(alias) == false)) {\n\t\t\t\t\t\t\t\t\t\tthrow new IllegalArgumentException(\"字符 \" + ck + \" 不合法！\"\n\t\t\t\t\t\t\t\t\t\t\t\t+ \"预编译模式下 @column:value 中 value里面用 , 分割的每一项\"\n\t\t\t\t\t\t\t\t\t\t\t\t+ \" column:alias 中 column 必须是1个单词！如果有alias，则alias也必须为1个单词！\"\n\t\t\t\t\t\t\t\t\t\t\t\t+ \"关键字必须全大写，且以空格分隔的参数，空格必须只有 1 个！其它情况不允许空格！\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tif (origin.startsWith(\"_\") || origin.contains(\"--\")) {\n\t\t\t\t\t\t\t\t\t\t// || PATTERN_FUNCTION.matcher(origin).matches() == false) {\n\t\t\t\t\t\t\t\t\t\tthrow new IllegalArgumentException(\"字符 \" + ck + \" 不合法！\"\n\t\t\t\t\t\t\t\t\t\t\t\t+ \"预编译模式下 @column:\\\"column0,column1:alias;function0(arg0,arg1,...);function1(...):alias...\\\"\"\n\t\t\t\t\t\t\t\t\t\t\t\t+ \" 中所有 arg 都必须是1个不以 _ 开头的单词 或者符合正则表达式 \"\n\t\t\t\t\t\t\t\t\t\t\t\t+ PATTERN_FUNCTION + \" 且不包含连续减号 -- ！\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\"DISTINCT 必须全大写，且后面必须有且只有 1 个空格！其它情况不允许空格！\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// 以空格分割参数\n\t\t\t\t\t\tString[] mkes = containRaw ? StringUtil.split(ck, \" \", true) : new String[]{ ck };\n\n\t\t\t\t\t\t//如果参数中含有空格(少数情况) 比如  fun(arg1, arg2,arg3,arg4) 中的 arg1 arg2 arg3，比如 DISTINCT id\n\t\t\t\t\t\tif (mkes != null && mkes.length >= 2) {\n\t\t\t\t\t\t\torigin = parseArgsSplitWithSpace(mkes);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tString mk = RAW_MAP.get(origin);\n\t\t\t\t\t\t\tif (mk != null) {  // newSQLConfig<T, M, L> 提前处理好的","sourceCodeStart":2720,"sourceCodeEnd":2756,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2720-L2756","documentation":"Thrown while parsing @column:\"...\" in SQLConfig when the request runs in prepared mode (isPrepared() == true). After splitting a column item on ':' into origin/alias, a non-column token (e.g. a function argument such as MAX(x)) is rejected because it starts with '_' or contains the consecutive minus '--'. This is an SQL-injection guard: '_' prefixes and '--' (SQL comment) are the classic payloads smuggled through column expressions.","triggerScenarios":"A GET/POST request body like {\"@column\":\"max(_score)\"} or {\"@column\":\"price--\"} (or any function-arg token in @column that starts with _ or contains --) while the Parser runs with prepared statements enabled (default in production via AbstractSQLExecutor). Only the non-isColumn branch (origin present, not simple column:alias) triggers it, at AbstractSQLConfig.java:2738.","commonSituations":"Developers copying a raw SQL fragment like 'DATE_ADD(date,INTERVAL -1 DAY)' or 'a-b' into @column; using columns that legitimately start with underscore; testing locally with raw mode then deploying to prepared mode where validation is stricter; attempt to comment out trailing SQL with --.","solutions":["Remove any '--' sequence: write subtraction as a single value or compute it in code, not inside @column (e.g. use 'price-1' is fine, 'price--1' is not).","If a real column name starts with '_', alias it in the database or map it via @column:\"`_col`\" only if raw mode is acceptable — in prepared mode rename the column instead.","Wrap the dynamic @column value in a whitelist check before sending the request (see validation code).","If you own the deployment and must allow such expressions, switch the table/request out of prepared mode only after reviewing the injection risk (setIsPrepared(false) is discouraged)."],"exampleFix":"// before\n{\"User\":{\"@column\":\"date_add(registerDate, INTERVAL _1 DAY):d, remark--\"}}\n// after\n{\"User\":{\"@column\":\"date_add(registerDate, INTERVAL 1 DAY):d, remark\"}}","handlingStrategy":"validation","validationCode":"String PATTERN_FUNCTION = \"^(\\\\w+\\\\(\\\\s*(DISTINCT\\\\s)?\\\\w+(\\\\s*,\\\\s*(\\\\w+|'[^']*'))*\\\\s*\\\\))$\";\nboolean ok = Arrays.stream(columnExpr.split(\",\")).allMatch(item -> {\n    String origin = item.contains(\":\") ? item.substring(0, item.indexOf(':')) : item;\n    return !origin.trim().startsWith(\"_\") && !origin.contains(\"--\")\n        && (origin.matches(\"\\\\w+\") || origin.matches(PATTERN_FUNCTION));\n});\nif (!ok) throw new IllegalArgumentException(\"unsafe @column item\");","typeGuard":"function isSafeColumnItem(item: string): boolean {\n  const origin = item.includes(':') ? item.slice(0, item.indexOf(':')) : item;\n  return !origin.startsWith('_') && !origin.includes('--') && /^[A-Za-z0-9_$]+$/.test(origin);\n}","tryCatchPattern":"catch (IllegalArgumentException e) { log.warn(\"rejected @column: {}\", columnExpr, e); /* fall back to a plain column list */ }","preventionTips":["Whitelist @column items against ^\\w+$ or the function pattern before sending.","Never interpolate user input into @column; map user choices to fixed column names.","Keep subtraction/SQL comments out of @column expressions."],"tags":["apijson","sql-injection","prepared-statement","column","validation"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}