From 9403d17e98a4dcddb7e9f2559ab67d56da361331 Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Sat, 11 Jul 2026 07:40:10 +0000
Subject: [PATCH] [#719] Fix NullPointerException decoding an ACI bind rule with a missing and/or operand (#720)
---
opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/BindRule.java | 31 ++++++++++++++++++++++++++++---
1 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/BindRule.java b/opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/BindRule.java
index 288a28f..96bbef5 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/BindRule.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/BindRule.java
@@ -142,7 +142,9 @@
/**
* Decode an ACI bind rule string representation.
* @param input The string representation of the bind rule.
- * @return A BindRule class representing the bind rule.
+ * @return A BindRule class representing the bind rule, or {@code null} if
+ * {@code input} is null or blank. This null is the recursion base case for
+ * an empty group; callers must reject it rather than dereference it.
* @throws AciException If the string is an invalid bind rule.
*/
public static BindRule decode (String input) throws AciException {
@@ -153,8 +155,10 @@
String bindruleStr = input.trim();
if (bindruleStr.isEmpty())
{
- // A blank bind rule (e.g. "( )") must be rejected as a
- // syntax error rather than throwing StringIndexOutOfBoundsException.
+ // A blank bind rule (e.g. "( )") decodes to null rather than
+ // throwing StringIndexOutOfBoundsException on charAt(0). This null is
+ // the recursion base case for an empty group; the caller rejects it as
+ // a syntax error (see the bindrule_1 == null check below).
return null;
}
char firstChar = bindruleStr.charAt(0);
@@ -203,6 +207,17 @@
throw new AciException(WARN_ACI_SYNTAX_BIND_RULE_MISSING_CLOSE_PAREN.get(input));
}
/*
+ * An empty group such as "()" or "( )" decodes to a null bind rule.
+ * Such a group is not a valid bind rule on its own, and using it as the
+ * left operand of an "and"/"or" (e.g. "()or userdn=...") would build a
+ * complex rule with a null left side that is later dereferenced during
+ * evaluation. Reject both cases here, before the remaining-chars test,
+ * so the message names the full offending input rather than a fragment.
+ */
+ if (bindrule_1 == null) {
+ throw new AciException(WARN_ACI_SYNTAX_INVALID_BIND_RULE_SYNTAX.get(input));
+ }
+ /*
* If there are remaining chars => there MUST be an operand (AND / OR)
* otherwise there is a syntax error
*/
@@ -318,6 +333,16 @@
boolean negate=determineNegation(ruleExpr);
remainingBindrule=ruleExpr.toString();
BindRule bindrule_2 = BindRule.decode(remainingBindrule);
+ /*
+ * The right operand of an "and"/"or" bind rule must exist. It is null
+ * when the boolean operator is not followed by a bind rule, e.g. the
+ * "or" in "userdn=\"ldap:///self\" or" has nothing to its right
+ * (issue #719).
+ */
+ if (bindrule_2 == null) {
+ throw new AciException(
+ WARN_ACI_SYNTAX_INVALID_BIND_RULE_SYNTAX.get(remainingBindruleStr));
+ }
bindrule_2.setNegate(negate);
return new BindRule(bindrule, bindrule_2, operand);
}
--
Gitblit v1.10.0