{"record":{"id":"8705a87b01e7660f","repo":"Tencent/WeKnora","slug":"access-to-system-column-s-is-not-allowed","errorCode":null,"errorMessage":"access to system column '%s' is not allowed","messagePattern":"access to system column '(.+?)' is not allowed","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/utils/inject.go","lineNumber":2346,"sourceCode":"\n\treturn nil\n}\n\n// validateColumnRef validates a column reference\nfunc (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}","sourceCodeStart":2328,"sourceCodeEnd":2364,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/utils/inject.go#L2328-L2364","documentation":"The identifier validator rejected a column reference to a PostgreSQL system column (xmin, xmax, cmin, cmax, ctid, tableoid). These columns expose internal row versioning and physical location metadata, which can be used for row-level probing and break portability, so they are blocked in injected expressions. Comparison is case-insensitive against the lowercased identifier.","triggerScenarios":"An expression referencing a system column such as SELECT xmin FROM t via the validator, or a WHERE/filter string like \"xmin::text LIKE '%'\", passed as a field identifier node with Sval matching one of the six system column names.","commonSituations":"Porting legacy Postgres queries that use xmin for optimistic concurrency or change detection; MVCC debugging queries copied into user-facing filters; attackers enumerating row visibility via ctid.","solutions":["Remove the system column reference and use an explicit version/timestamp column maintained by the application","Implement optimistic locking with an app-level updated_at/revision column instead of xmin","Use the database's native API (e.g. direct SQL session, not the injection validator) if system column access is truly required for DBA work","If filtering duplicates, use DISTINCT or an allowlisted unique key rather than ctid"],"exampleFix":"// before\nfilter := \"xmin > 100\"\n// after\nfilter := \"revision > 100\" // app-managed version column","handlingStrategy":"validation","validationCode":"var systemColumns = []string{\"xmin\", \"xmax\", \"cmin\", \"cmax\", \"ctid\", \"tableoid\"}\nfunc referencesSystemColumn(col string) bool {\n    c := strings.ToLower(col)\n    return slices.Contains(systemColumns, c)\n}","typeGuard":"func isSystemColumn(name string) bool {\n    switch strings.ToLower(name) {\n    case \"xmin\", \"xmax\", \"cmin\", \"cmax\", \"ctid\", \"tableoid\":\n        return true\n    }\n    return false\n}","tryCatchPattern":"if err := injector.Validate(expr); err != nil {\n    if strings.Contains(err.Error(), \"system column\") {\n        http.Error(w, \"system columns cannot be used in filters; use an application-managed version column\", http.StatusBadRequest)\n        return\n    }\n    return\n}","preventionTips":["Use app-managed revision/updated_at columns for optimistic concurrency instead of xmin","Document that MVCC internals are off-limits in user-facing expressions","Checklist-review migrated Postgres queries for system column usage before enabling them in filters"],"tags":["sql-injection","postgres","system-columns","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"}