{"record":{"id":"88586825158f8975","repo":"apache/druid","slug":"failed-to-sanitize-searchresult-in-context-key-s","errorCode":null,"errorMessage":"Failed to sanitize SearchResult in context key [%s]","messagePattern":"Failed to sanitize SearchResult in context key \\[(.+?)\\]","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"extensions-contrib/druid-opa-authorizer/src/main/java/org/apache/druid/security/opa/OpaAuthorizer.java","lineNumber":199,"sourceCode":"    catch (Exception e) {\n      return Access.deny(\"An error occurred: \" + e);\n    }\n  }\n\n  protected Map<String, Object> sanitizeContext(Map<String, Object> context)\n  {\n    if (context == null || context.isEmpty()) {\n      return context;\n    }\n\n    final Map<String, Object> sanitizedContext = new HashMap<>();\n    for (final Map.Entry<String, Object> entry : context.entrySet()) {\n      if (entry.getValue() instanceof SearchResult) {\n        try {\n          sanitizedContext.put(entry.getKey(), sanitizeSearchResult((SearchResult) entry.getValue()));\n        }\n        catch (NamingException e) {\n          LOG.warn(e, \"Failed to sanitize SearchResult in context key [%s]\", entry.getKey());\n          sanitizedContext.put(entry.getKey(), Collections.emptyMap());\n        }\n      } else {\n        // Keep other types as is, assuming they are serializable or handled by other means\n        sanitizedContext.put(entry.getKey(), entry.getValue());\n      }\n    }\n    return sanitizedContext;\n  }\n\n  private Map<String, Object> sanitizeSearchResult(SearchResult searchResult) throws NamingException\n  {\n    final Map<String, Object> sanitized = new HashMap<>();\n    sanitized.put(\"name\", searchResult.getName());\n\n    try {\n      sanitized.put(\"nameInNamespace\", searchResult.getNameInNamespace());\n    }","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/extensions-contrib/druid-opa-authorizer/src/main/java/org/apache/druid/security/opa/OpaAuthorizer.java#L181-L217","documentation":"During OPA authorization, LDAP SearchResult objects found in the request context are sanitized to remove attributes not allowed to be forwarded. If sanitizeSearchResult throws a NamingException (LDAP/JNDI failure while reading attributes), the emitter logs this warning and substitutes an empty map for that context key so authorization can proceed.","triggerScenarios":"A context entry value is a javax.naming.directory.SearchResult and the underlying LDAP directory rejects the attribute read: closed or timed-out LDAP connection, referral chasing failure, schema violation reading a specific attribute, or a null/invalid SearchResult supplied by an LDAP extension into the auth context.","commonSituations":"Deployments using LDAP-based authentication feeding the OPA authorizer; misconfigured LDAP connection pools going stale, firewalls dropping idle LDAP connections, or LDAP servers returning partial results/referrals that JNDI cannot follow.","solutions":["Verify LDAP connectivity and keep-alive settings between the Druid process and the LDAP server; the sanitized value degrades to an empty map, so expect context attributes to be missing downstream.","Inspect the warn-level NamingException stack trace to identify which attribute or referral failed and fix the LDAP schema/referral configuration.","Increase connection pool timeouts (com.sun.jndi.ldap.connect.timeout / read timeout) so reads do not fail mid-sanitization.","If the empty map breaks OPA policy decisions, update the OPA policy to tolerate a missing/empty context attribute instead of failing the request."],"exampleFix":"// before\nsanitizedContext.put(entry.getKey(), Collections.emptyMap());\n// after\nSearchResult sr = (SearchResult) entry.getValue();\nif (sr.getAttributes() != null) {\n  sanitizedContext.put(entry.getKey(), sanitizeSearchResult(sr));\n} else {\n  sanitizedContext.put(entry.getKey(), Collections.emptyMap());\n}","handlingStrategy":"fallback","validationCode":"if (value instanceof SearchResult) {\n  SearchResult sr = (SearchResult) value;\n  if (sr.getAttributes() == null) {\n    log.warn(\"SearchResult for key %s has null attributes; will sanitize to empty map\", key);\n  }\n}","typeGuard":"static boolean isSanitizableSearchResult(Object v) {\n  return v instanceof SearchResult && ((SearchResult) v).getAttributes() != null;\n}","tryCatchPattern":"try {\n  ctx.put(key, sanitizeSearchResult((SearchResult) value));\n} catch (NamingException e) {\n  log.warn(e, \"LDAP sanitize failed for key %s; using empty map\", key);\n  ctx.put(key, Collections.emptyMap());\n}","preventionTips":["Keep LDAP connection pools warm; set connect/read timeouts explicitly.","Handle LDAP referrals deliberately (java.naming.referral=follow/throw) to avoid surprise NamingExceptions.","Alert on this warning: a burst indicates LDAP connectivity degradation.","Design OPA policies to tolerate missing/empty context attributes."],"tags":["ldap","jndi","sanitization","authorization"],"backgroundTag":"ldap-connection-failed","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}