{"record":{"id":"9c47bf5523993c12","repo":"Tencent/WeKnora","slug":"access-to-s-is-not-allowed","errorCode":null,"errorMessage":"access to '%s' is not allowed","messagePattern":"access to '(.+?)' is not allowed","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/utils/inject.go","lineNumber":2351,"sourceCode":"func (v *sqlValidator) validateColumnRef(cr *pg_query.ColumnRef) error {\n\tif !v.checkSystemColumns {\n\t\treturn nil\n\t}\n\n\t// Check for system column access\n\tfor _, field := range cr.Fields {\n\t\tif s := field.GetString_(); s != nil {\n\t\t\tcolName := strings.ToLower(s.Sval)\n\t\t\t// Block access to system columns\n\t\t\tsystemColumns := []string{\"xmin\", \"xmax\", \"cmin\", \"cmax\", \"ctid\", \"tableoid\"}\n\t\t\tfor _, sysCol := range systemColumns {\n\t\t\t\tif colName == sysCol {\n\t\t\t\t\treturn fmt.Errorf(\"access to system column '%s' is not allowed\", colName)\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Block pg_ prefixed identifiers\n\t\t\tif strings.HasPrefix(colName, \"pg_\") {\n\t\t\t\treturn fmt.Errorf(\"access to '%s' is not allowed\", colName)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// getTypeName extracts the type name from a TypeName node\nfunc (v *sqlValidator) getTypeName(tn *pg_query.TypeName) string {\n\tvar parts []string\n\tfor _, name := range tn.Names {\n\t\tif s := name.GetString_(); s != nil {\n\t\t\tparts = append(parts, s.Sval)\n\t\t}\n\t}\n\treturn strings.Join(parts, \".\")\n}\n","sourceCodeStart":2333,"sourceCodeEnd":2368,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/utils/inject.go#L2333-L2368","documentation":"The identifier validator rejected a column whose lowercased name starts with pg_, reserving PostgreSQL's pg_* namespace (system catalogs like pg_catalog, pg_class, pg_attribute). Blocking this prefix prevents injected expressions from referencing catalog tables or shadowing catalog functions for information disclosure. It follows the exact-match system column check in the same function.","triggerScenarios":"An expression or filter containing an identifier like pg_sleep, pg_read_file, pg_catalog.pg_class, or a user column accidentally named pg_owner passed through the identifier validation path (field.GetString_() branch).","commonSituations":"Time-based injection attempts using pg_sleep; catalog-enumeration queries (pg_tables, pg_user) pasted into filters; legitimate columns whose names collide with the pg_ prefix after a schema rename.","solutions":["Rename any application column that starts with pg_ to a non-reserved name","Remove pg_* function/table references from the expression; use allowlisted equivalents","For catalog inspection, connect directly with a DBA tool instead of validated expressions","Audit generated SQL from ORMs/LLMs for pg_-prefixed helpers before submitting"],"exampleFix":"// before\nfilter := \"pg_sleep(5) IS NULL\"\n// after\n// remove the call entirely; delay tactics have no valid use in filters\nfilter := \"status = 'active'\"","handlingStrategy":"validation","validationCode":"func usesPgReservedPrefix(ident string) bool {\n    return strings.HasPrefix(strings.ToLower(ident), \"pg_\")\n}\n// reject before submission\nif usesPgReservedPrefix(columnName) { return errors.New(\"identifiers must not start with pg_\") }","typeGuard":"func isSafeIdentifier(name string) bool {\n    if name == \"\" { return false }\n    n := strings.ToLower(name)\n    if strings.HasPrefix(n, \"pg_\") { return false }\n    for _, c := range n {\n        if !(c == '_' || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) { return false }\n    }\n    return true\n}","tryCatchPattern":"if err := injector.Validate(expr); err != nil {\n    if strings.Contains(err.Error(), \"is not allowed\") && strings.Contains(strings.ToLower(err.Error()), \"pg_\") {\n        return fmt.Errorf(\"identifier uses the reserved pg_ prefix: %w\", err)\n    }\n    return err\n}","preventionTips":["Lint schemas to forbid pg_-prefixed column/table names at migration time","Strip catalog references from generated SQL before validation","Keep an internal lint rule flagging pg_* function calls in expression templates"],"tags":["sql-injection","postgres","reserved-prefix","security"],"backgroundTag":"system-column-access-blocked","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}