{"record":{"id":"71d33eb72b552a28","repo":"Tencent/APIJSON","slug":"key-71d33e","errorCode":null,"errorMessage":"参数 {} 不合法！key 中不允许有单引号 ' ！","messagePattern":"参数 (.+?) 不合法！key 中不允许有单引号 ' ！","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java","lineNumber":4077,"sourceCode":"\t\t}\n\n\t\treturn gainKey(column) + \" \" + type + \" \" + (value instanceof Subquery ? gainSubqueryString((Subquery<T, M, L>) value)\n\t\t\t\t: (rawSQL != null ? rawSQL : gainValue(key, column, value)));\n\t}\n\n\tpublic String gainKey(@NotNull String key) {\n\t\tString lenFun = \"\";\n\t\tif (key.endsWith(\"[\")) {\n\t\t\tlenFun = isSQLServer() || isKingBaseSQLServer() ? \"datalength\" : \"length\";\n\t\t\tkey = key.substring(0, key.length() - 1);\n\t\t}\n\t\telse if (key.endsWith(\"{\")) {\n\t\t\tlenFun = \"json_length\";\n\t\t\tkey = key.substring(0, key.length() - 1);\n\t\t}\n\t\telse if (isTest()) {\n\t\t\tif (key.contains(\"'\")) {  // || key.contains(\"#\") || key.contains(\"--\")) {\n\t\t\t\tthrow new IllegalArgumentException(\"参数 \" + key + \" 不合法！key 中不允许有单引号 ' ！\");\n\t\t\t}\n\t\t\treturn gainSQLValue(key).toString();\n\t\t}\n\n\t\tMap<String, String> keyMap = getKeyMap();\n\t\tString expression = keyMap == null ? null : keyMap.get(key);\n\t\tif (expression == null) {\n\t\t\texpression = COLUMN_KEY_MAP == null ? null : COLUMN_KEY_MAP.get(key);\n\t\t}\n\n\t\tString sqlKey;\n\t\tif (expression == null) {\n\t\t\tsqlKey = gainSQLKey(key);\n\t\t}\n\t\telse {\n\t\t\t// (name,tag) left(date,4) 等\n\t\t\tList<String> raw = getRaw();\n\t\t\tsqlKey = parseSQLExpression(KEY_KEY, expression, raw != null && raw.contains(KEY_KEY), false);","sourceCodeStart":4059,"sourceCodeEnd":4095,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L4059-L4095","documentation":"Thrown by gainKey only when isTest() is true: the key contains a single quote character. In test mode the library passes keys through gainSQLValue directly (no prepared-statement placeholder path), so a quote could break out of the string and is rejected as an SQL-injection guard.","triggerScenarios":"Running with the test/debug flag enabled (isTest()) and sending a condition key or @column expression containing ' — e.g. dynamically built keys like \"name'\" or raw column fragments with quotes.","commonSituations":"Unit/integration tests that enable test mode and then feed user-controlled key strings; leftover isTest configuration in a deployment pipeline.","solutions":["Remove single quotes from keys/values; escape or drop them client-side.","If the quote is legitimate data, pass it as a prepared value (condition value), not as part of the key.","Verify isTest() is not accidentally enabled in production or CI configuration."],"exampleFix":"// before\n{\"User\": {\"name'\": \"az\"}}\n// after\n{\"User\": {\"name\": \"az\"}}","handlingStrategy":"validation","validationCode":"if (isTestMode && key.includes(\"'\")) throw new Error(\"key must not contain a single quote in test mode\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Sanitize all user-supplied key fragments: strip quotes before building requests.","Keep quotes in values (prepared), never in keys.","Audit where isTest() gets enabled; keep it off in shared environments."],"tags":["apijson","sql-injection","test-mode","key-sanitization"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}