You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by ma...@apache.org on 2022/12/08 02:48:02 UTC

[ranger] branch master updated: RANGER-3997: option to use default value when user/group/tag does not have the attribute

This is an automated email from the ASF dual-hosted git repository.

madhan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git


The following commit(s) were added to refs/heads/master by this push:
     new e1cdc6fd7 RANGER-3997: option to use default value when user/group/tag does not have the attribute
e1cdc6fd7 is described below

commit e1cdc6fd738f930e6cc7228ccc4bd0855ecc6847
Author: Madhan Neethiraj <ma...@apache.org>
AuthorDate: Mon Dec 5 15:02:38 2022 -0800

    RANGER-3997: option to use default value when user/group/tag does not have the attribute
---
 .../policyengine/RangerRequestScriptEvaluator.java | 251 +++++++++++++++++----
 .../ranger/plugin/util/RangerCommonConstants.java  |  20 ++
 .../RangerRequestScriptEvaluatorTest.java          | 214 ++++++++++++++++++
 3 files changed, 444 insertions(+), 41 deletions(-)

diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerRequestScriptEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerRequestScriptEvaluator.java
index 71802662f..7ac20764f 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerRequestScriptEvaluator.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerRequestScriptEvaluator.java
@@ -70,8 +70,8 @@ public final class RangerRequestScriptEvaluator {
 	private static final Pattern JSON_VAR_NAMES_PATTERN   = Pattern.compile(getJsonVarNamesPattern());
 	private static final Pattern USER_ATTRIBUTES_PATTERN  = Pattern.compile(getUserAttributesPattern());
 	private static final Pattern GROUP_ATTRIBUTES_PATTERN = Pattern.compile(getGroupAttributesPattern());
-	private static final Character CHAR_QUOTE = '\'';
-	private static final Character CHAR_COMMA = ',';
+	private static final String  STR_QUOTE  = "'";
+	private static final String  STR_COMMA  = ",";
 
 	private static final MacroProcessor MACRO_PROCESSOR = new MacroProcessor(getMacrosMap());
 
@@ -633,98 +633,116 @@ public final class RangerRequestScriptEvaluator {
 		return ret;
 	}
 
-	public String ugNamesCsv() {
+	public String tagNames(Object... args) {
 		init();
 
-		return toCsv(userGroups);
+		return toCsv(tagNames, args);
 	}
 
-	public String ugNamesCsvQ() {
+	public String tagNamesQ(Object... args) {
 		init();
 
-		return toCsvQ(userGroups);
+		return toCsvQ(tagNames, args);
 	}
 
-	public String urNamesCsv() {
+	public String tagAttrNames(Object... args) {
 		init();
 
-		return toCsv(userRoles);
+		return toCsv(getTagAttrNames(), args);
 	}
 
-	public String urNamesCsvQ() {
+	public String tagAttrNamesQ(Object... args) {
 		init();
 
-		return toCsvQ(userRoles);
+		return toCsvQ(getTagAttrNames(), args);
 	}
 
-	public String tagNamesCsv() {
+	public String tagAttr(String attrName, Object... args) {
 		init();
 
-		return toCsv(tagNames);
+		return toCsv(getTagAttr(attrName), args);
 	}
 
-	public String tagNamesCsvQ() {
+	public String tagAttrQ(String attrName, Object... args) {
 		init();
 
-		return toCsvQ(tagNames);
+		return toCsvQ(getTagAttr(attrName), args);
 	}
 
-	public String userAttrNamesCsv() {
+	public String ugNames(Object... args) {
 		init();
 
-		return toCsv(getUserAttrNames());
+		return toCsv(userGroups, args);
 	}
 
-	public String userAttrNamesCsvQ() {
+	public String ugNamesQ(Object... args) {
 		init();
 
-		return toCsvQ(getUserAttrNames());
+		return toCsvQ(userGroups, args);
 	}
 
-	public String ugAttrNamesCsv() {
+	public String ugAttrNames(Object... args) {
 		init();
 
-		return toCsv(getUgAttrNames());
+		return toCsv(getUgAttrNames(), args);
 	}
 
-	public String ugAttrNamesCsvQ() {
-		return toCsvQ(getUgAttrNames());
+	public String ugAttrNamesQ(Object... args) {
+		init();
+
+		return toCsvQ(getUgAttrNames(), args);
 	}
 
-	public String tagAttrNamesCsv() {
+	public String ugAttr(String attrName, Object... args) {
 		init();
 
-		return toCsv(getTagAttrNames());
+		return toCsv(getUgAttr(attrName), args);
 	}
 
-	public String tagAttrNamesCsvQ() {
+	public String ugAttrQ(String attrName, Object... args) {
 		init();
 
-		return toCsvQ(getTagAttrNames());
+		return toCsvQ(getUgAttr(attrName), args);
 	}
 
-	public String ugAttrCsv(String attrName) {
+	public String urNames(Object... args) {
 		init();
 
-		return toCsv(getUgAttr(attrName));
+		return toCsv(userRoles, args);
 	}
 
-	public String ugAttrCsvQ(String attrName) {
+	public String urNamesQ(Object... args) {
 		init();
 
-		return toCsvQ(getUgAttr(attrName));
+		return toCsvQ(userRoles, args);
 	}
 
-	public String tagAttrCsv(String attrName) {
+	public String userAttrNames(Object... args) {
 		init();
 
-		return toCsv(getTagAttr(attrName));
+		return toCsv(getUserAttrNames(), args);
 	}
 
-	public String tagAttrCsvQ(String attrName) {
+	public String userAttrNamesQ(Object... args) {
+		init();
+
+		return toCsvQ(getUserAttrNames(), args);
+	}
+
+	public String userAttr(String attrName, Object... args) {
+		init();
+
+		String attrVal = userAttrs.get(attrName);
+
+		return toCsv(Collections.singletonList(attrVal), args);
+	}
+
+	public String userAttrQ(String attrName, Object... args) {
 		init();
 
-		return toCsvQ(getTagAttr(attrName));
+		String attrVal = userAttrs.get(attrName);
+
+		return toCsvQ(Collections.singletonList(attrVal), args);
 	}
 
 	public boolean hasTag(String tagName) {
@@ -804,6 +822,71 @@ public final class RangerRequestScriptEvaluator {
 		return !userRoles.isEmpty();
 	}
 
+	// for backward compatibility
+	public String ugNamesCsv() {
+		return ugNames(null, STR_COMMA);
+	}
+
+	public String ugNamesCsvQ() {
+		return ugNamesQ(null, STR_COMMA, STR_QUOTE);
+	}
+
+	public String urNamesCsv() {
+		return urNames(null, STR_COMMA);
+	}
+
+	public String urNamesCsvQ() {
+		return urNamesQ(null, STR_COMMA, STR_QUOTE);
+	}
+
+	public String tagNamesCsv() {
+		return tagNames(null, STR_COMMA);
+	}
+
+	public String tagNamesCsvQ() {
+		return tagNamesQ(null, STR_COMMA, STR_QUOTE);
+	}
+
+	public String userAttrNamesCsv() {
+		return userAttrNames(null, STR_COMMA);
+	}
+
+	public String userAttrNamesCsvQ() {
+		return userAttrNamesQ(null, STR_COMMA, STR_QUOTE);
+	}
+
+	public String ugAttrNamesCsv() {
+		return ugAttrNames(null, STR_COMMA);
+	}
+
+	public String ugAttrNamesCsvQ() {
+		return ugAttrNamesQ(null, STR_COMMA, STR_QUOTE);
+	}
+
+	public String tagAttrNamesCsv() {
+		return tagAttrNames(null, STR_COMMA);
+	}
+
+	public String tagAttrNamesCsvQ() {
+		return tagAttrNamesQ(null, STR_COMMA, STR_QUOTE);
+	}
+
+	public String ugAttrCsv(String attrName) {
+		return ugAttr(attrName, null, STR_COMMA);
+	}
+
+	public String ugAttrCsvQ(String attrName) {
+		return ugAttrQ(attrName, null, STR_COMMA, STR_QUOTE);
+	}
+
+	public String tagAttrCsv(String attrName) {
+		return tagAttr(attrName, null, STR_COMMA);
+	}
+
+	public String tagAttrCsvQ(String attrName) {
+		return tagAttrQ(attrName, null, STR_COMMA, STR_QUOTE);
+	}
+
 	private void init() {
 		if (!initDone) {
 			RangerUserStore                  userStore        = RangerAccessRequestUtil.getRequestUserStoreFromContext(accessRequest.getContext());
@@ -965,8 +1048,9 @@ public final class RangerRequestScriptEvaluator {
 		return getSorted(ret);
 	}
 
-	private String toCsv(Collection<? extends Object> values) {
-		StringBuilder sb = new StringBuilder();
+	private String toCsv(Collection<? extends Object> values, Object[] args) {
+		StringBuilder sb        = new StringBuilder();
+		String        separator = getSeparator(args);
 
 		for (Object value : values) {
 			if (value == null) {
@@ -974,17 +1058,28 @@ public final class RangerRequestScriptEvaluator {
 			}
 
 			if (sb.length() > 0) {
-				sb.append(CHAR_COMMA);
+				sb.append(separator);
 			}
 
 			sb.append(value);
 		}
 
+		if (sb.length() == 0) {
+			String defValue = getDefaultValue(args);
+
+			if (defValue != null) {
+				sb.append(getDefaultValue(args));
+			}
+		}
+
 		return sb.toString();
 	}
 
-	private String toCsvQ(Collection<? extends Object> values) {
-		StringBuilder sb = new StringBuilder();
+	private String toCsvQ(Collection<? extends Object> values, Object[] args) {
+		StringBuilder sb         = new StringBuilder();
+		String        openQuote  = getOpenQuote(args);
+		String        closeQuote = getCloseQuote(args, openQuote);
+		String        separator  = getSeparator(args);
 
 		for (Object value : values) {
 			if (value == null) {
@@ -992,15 +1087,47 @@ public final class RangerRequestScriptEvaluator {
 			}
 
 			if (sb.length() > 0) {
-				sb.append(CHAR_COMMA);
+				sb.append(separator);
 			}
 
-			sb.append(CHAR_QUOTE).append(value).append(CHAR_QUOTE);
+			sb.append(openQuote).append(value).append(closeQuote);
+		}
+
+		if (sb.length() == 0) {
+			String defValue = getDefaultValue(args);
+
+			if (defValue != null) {
+				sb.append(openQuote).append(getDefaultValue(args)).append(closeQuote);
+			}
 		}
 
 		return sb.toString();
 	}
 
+	private String getDefaultValue(Object[] args) {
+		Object ret = (args != null && args.length > 0) ? args[0] : null;
+
+		return ret != null ? ret.toString() : null;
+	}
+
+	private String getSeparator(Object[] args) {
+		Object ret = (args != null && args.length > 1) ? args[1] : STR_COMMA;
+
+		return ret != null ? ret.toString() : "";
+	}
+
+	private String getOpenQuote(Object[] args) {
+		Object ret = (args != null && args.length > 2) ? args[2] : STR_QUOTE;
+
+		return ret != null ? ret.toString() : "";
+	}
+
+	private String getCloseQuote(Object[] args, String openQuote) {
+		Object ret = (args != null && args.length > 3) ? args[3] : null;
+
+		return ret != null ? ret.toString() : openQuote;
+	}
+
 	private static String getJsonVarNamesPattern() {
 		List<String> varNames = new ArrayList<>();
 
@@ -1030,10 +1157,21 @@ public final class RangerRequestScriptEvaluator {
 		List<String> varNames = new ArrayList<>();
 
 		varNames.add(SCRIPT_VAR_USER);
+
+		varNames.add(SCRIPT_MACRO_GET_USER_ATTR);
+		varNames.add(SCRIPT_MACRO_GET_USER_ATTR_Q);
+
+		varNames.add(SCRIPT_MACRO_GET_USER_ATTR_NAMES);
+		varNames.add(SCRIPT_MACRO_GET_USER_ATTR_NAMES_Q);
 		varNames.add(SCRIPT_MACRO_USER_ATTR_NAMES_CSV);
 		varNames.add(SCRIPT_MACRO_USER_ATTR_NAMES_Q_CSV);
+
 		varNames.add(SCRIPT_MACRO_HAS_USER_ATTR);
 
+		varNames.add("userAttr");
+		varNames.add("userAttrQ");
+		varNames.add("userAttrNames");
+		varNames.add("userAttrNamesQ");
 		varNames.add("userAttrNamesCsv");
 		varNames.add("userAttrNamesCsvQ");
 		varNames.add("hasUserAttr");
@@ -1047,16 +1185,28 @@ public final class RangerRequestScriptEvaluator {
 		varNames.add(SCRIPT_VAR_UG);
 		varNames.add(SCRIPT_VAR_UGA);
 
+		varNames.add(SCRIPT_MACRO_GET_UG_ATTR);
+		varNames.add(SCRIPT_MACRO_GET_UG_ATTR_Q);
 		varNames.add(SCRIPT_MACRO_GET_UG_ATTR_CSV);
 		varNames.add(SCRIPT_MACRO_GET_UG_ATTR_Q_CSV);
+
+		varNames.add(SCRIPT_MACRO_GET_UG_ATTR_NAMES);
+		varNames.add(SCRIPT_MACRO_GET_UG_ATTR_NAMES_Q);
 		varNames.add(SCRIPT_MACRO_UG_ATTR_NAMES_CSV);
 		varNames.add(SCRIPT_MACRO_UG_ATTR_NAMES_Q_CSV);
+
 		varNames.add(SCRIPT_MACRO_HAS_UG_ATTR);
 
+		varNames.add("ugAttr");
+		varNames.add("ugAttrQ");
 		varNames.add("ugAttrCsv");
 		varNames.add("ugAttrCsvQ");
+
+		varNames.add("ugAttrNames");
+		varNames.add("ugAttrNamesQ");
 		varNames.add("ugAttrNamesCsv");
 		varNames.add("ugAttrNamesCsvQ");
+
 		varNames.add("hasUgAttr");
 
 		return "\\b(" + StringUtils.join(varNames, '|') + ")\\b";
@@ -1065,6 +1215,25 @@ public final class RangerRequestScriptEvaluator {
 	private static Map<String, String> getMacrosMap() {
 		Map<String, String> ret = new HashMap<>();
 
+		ret.put(SCRIPT_MACRO_GET_TAG_NAMES,         "ctx.tagNames");
+		ret.put(SCRIPT_MACRO_GET_TAG_NAMES_Q,       "ctx.tagNamesQ");
+		ret.put(SCRIPT_MACRO_GET_TAG_ATTR_NAMES,    "ctx.tagAttrNames");
+		ret.put(SCRIPT_MACRO_GET_TAG_ATTR_NAMES_Q,  "ctx.tagAttrNamesQ");
+		ret.put(SCRIPT_MACRO_GET_TAG_ATTR,          "ctx.tagAttr");
+		ret.put(SCRIPT_MACRO_GET_TAG_ATTR_Q,        "ctx.tagAttrQ");
+		ret.put(SCRIPT_MACRO_GET_UG_NAMES,          "ctx.ugNames");
+		ret.put(SCRIPT_MACRO_GET_UG_NAMES_Q,        "ctx.ugNamesQ");
+		ret.put(SCRIPT_MACRO_GET_UG_ATTR_NAMES,     "ctx.ugAttrNames");
+		ret.put(SCRIPT_MACRO_GET_UG_ATTR_NAMES_Q,   "ctx.ugAttrNamesQ");
+		ret.put(SCRIPT_MACRO_GET_UG_ATTR,           "ctx.ugAttr");
+		ret.put(SCRIPT_MACRO_GET_UG_ATTR_Q,         "ctx.ugAttrQ");
+		ret.put(SCRIPT_MACRO_GET_UR_NAMES,          "ctx.urNames");
+		ret.put(SCRIPT_MACRO_GET_UR_NAMES_Q,        "ctx.urNamesQ");
+		ret.put(SCRIPT_MACRO_GET_USER_ATTR_NAMES,   "ctx.userAttrNames");
+		ret.put(SCRIPT_MACRO_GET_USER_ATTR_NAMES_Q, "ctx.userAttrNamesQ");
+		ret.put(SCRIPT_MACRO_GET_USER_ATTR,         "ctx.userAttr");
+		ret.put(SCRIPT_MACRO_GET_USER_ATTR_Q,       "ctx.userAttrQ");
+
 		ret.put(SCRIPT_MACRO_GET_TAG_ATTR_CSV,      "ctx.tagAttrCsv");
 		ret.put(SCRIPT_MACRO_GET_TAG_ATTR_Q_CSV,    "ctx.tagAttrCsvQ");
 		ret.put(SCRIPT_MACRO_GET_UG_ATTR_CSV,       "ctx.ugAttrCsv");
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerCommonConstants.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerCommonConstants.java
index 73e3dadbf..54991deca 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerCommonConstants.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerCommonConstants.java
@@ -99,6 +99,25 @@ public class RangerCommonConstants {
 	public static final String SCRIPT_FIELD_UGA                     = "uga";
 	public static final String SCRIPT_FIELD_USER_ROLES              = "userRoles";
 
+	public static final String SCRIPT_MACRO_GET_TAG_NAMES         = "GET_TAG_NAMES";
+	public static final String SCRIPT_MACRO_GET_TAG_NAMES_Q       = "GET_TAG_NAMES_Q";
+	public static final String SCRIPT_MACRO_GET_TAG_ATTR_NAMES    = "GET_TAG_ATTR_NAMES";
+	public static final String SCRIPT_MACRO_GET_TAG_ATTR_NAMES_Q  = "GET_TAG_ATTR_NAMES_Q";
+	public static final String SCRIPT_MACRO_GET_TAG_ATTR          = "GET_TAG_ATTR";
+	public static final String SCRIPT_MACRO_GET_TAG_ATTR_Q        = "GET_TAG_ATTR_Q";
+	public static final String SCRIPT_MACRO_GET_UG_NAMES          = "GET_UG_NAMES";
+	public static final String SCRIPT_MACRO_GET_UG_NAMES_Q        = "GET_UG_NAMES_Q";
+	public static final String SCRIPT_MACRO_GET_UG_ATTR_NAMES     = "GET_UG_ATTR_NAMES";
+	public static final String SCRIPT_MACRO_GET_UG_ATTR_NAMES_Q   = "GET_UG_ATTR_NAMES_Q";
+	public static final String SCRIPT_MACRO_GET_UG_ATTR           = "GET_UG_ATTR";
+	public static final String SCRIPT_MACRO_GET_UG_ATTR_Q         = "GET_UG_ATTR_Q";
+	public static final String SCRIPT_MACRO_GET_UR_NAMES          = "GET_UR_NAMES";
+	public static final String SCRIPT_MACRO_GET_UR_NAMES_Q        = "GET_UR_NAMES_Q";
+	public static final String SCRIPT_MACRO_GET_USER_ATTR_NAMES   = "GET_USER_ATTR_NAMES";
+	public static final String SCRIPT_MACRO_GET_USER_ATTR_NAMES_Q = "GET_USER_ATTR_NAMES_Q";
+	public static final String SCRIPT_MACRO_GET_USER_ATTR         = "GET_USER_ATTR";
+	public static final String SCRIPT_MACRO_GET_USER_ATTR_Q       = "GET_USER_ATTR_Q";
+
 	public static final String SCRIPT_MACRO_GET_TAG_ATTR_CSV      = "GET_TAG_ATTR_CSV";
 	public static final String SCRIPT_MACRO_GET_TAG_ATTR_Q_CSV    = "GET_TAG_ATTR_Q_CSV";
 	public static final String SCRIPT_MACRO_GET_UG_ATTR_CSV       = "GET_UG_ATTR_CSV";
@@ -115,6 +134,7 @@ public class RangerCommonConstants {
 	public static final String SCRIPT_MACRO_UR_NAMES_Q_CSV        = "UR_NAMES_Q_CSV";
 	public static final String SCRIPT_MACRO_USER_ATTR_NAMES_CSV   = "USER_ATTR_NAMES_CSV";
 	public static final String SCRIPT_MACRO_USER_ATTR_NAMES_Q_CSV = "USER_ATTR_NAMES_Q_CSV";
+
 	public static final String SCRIPT_MACRO_HAS_TAG               = "HAS_TAG";
 	public static final String SCRIPT_MACRO_HAS_ANY_TAG           = "HAS_ANY_TAG";
 	public static final String SCRIPT_MACRO_HAS_NO_TAG            = "HAS_NO_TAG";
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerRequestScriptEvaluatorTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerRequestScriptEvaluatorTest.java
index c398e0205..6705327d8 100644
--- a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerRequestScriptEvaluatorTest.java
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerRequestScriptEvaluatorTest.java
@@ -135,6 +135,220 @@ public class RangerRequestScriptEvaluatorTest {
         Assert.assertFalse("test: HAS_TAG(notExists)", (Boolean) evaluator.evaluateScript(scriptEngine, "HAS_TAG('notExists')"));
         Assert.assertTrue("test: HAS_ANY_TAG", (Boolean) evaluator.evaluateScript(scriptEngine, "HAS_ANY_TAG"));
         Assert.assertFalse("test: HAS_NO_TAG", (Boolean) evaluator.evaluateScript(scriptEngine, "HAS_NO_TAG"));
+
+        Assert.assertEquals("GET_TAG_NAMES()",           "PCI,PII", evaluator.evaluateScript(scriptEngine, "GET_TAG_NAMES()"));
+        Assert.assertEquals("GET_TAG_NAMES(null)",       "PCI,PII", evaluator.evaluateScript(scriptEngine, "GET_TAG_NAMES(null)"));
+        Assert.assertEquals("GET_TAG_NAMES(null, '|')",  "PCI|PII", evaluator.evaluateScript(scriptEngine, "GET_TAG_NAMES(null, '|')"));
+        Assert.assertEquals("GET_TAG_NAMES(null, null)", "PCIPII", evaluator.evaluateScript(scriptEngine, "GET_TAG_NAMES(null, null)"));
+
+        Assert.assertEquals("GET_TAG_NAMES_Q()",                    "'PCI','PII'", evaluator.evaluateScript(scriptEngine, "GET_TAG_NAMES_Q()"));
+        Assert.assertEquals("GET_TAG_NAMES_Q(null)",                "'PCI','PII'", evaluator.evaluateScript(scriptEngine, "GET_TAG_NAMES_Q(null)"));
+        Assert.assertEquals("GET_TAG_NAMES_Q(null, '|')",           "'PCI'|'PII'", evaluator.evaluateScript(scriptEngine, "GET_TAG_NAMES_Q(null, '|')"));
+        Assert.assertEquals("GET_TAG_NAMES_Q(null, null)",          "'PCI''PII'",  evaluator.evaluateScript(scriptEngine, "GET_TAG_NAMES_Q(null, null)"));
+        Assert.assertEquals("GET_TAG_NAMES_Q(null, '|', null)",     "PCI|PII",     evaluator.evaluateScript(scriptEngine, "GET_TAG_NAMES_Q(null, '|', null)"));
+        Assert.assertEquals("GET_TAG_NAMES_Q(null, ',', '{', '}')", "{PCI},{PII}", evaluator.evaluateScript(scriptEngine, "GET_TAG_NAMES_Q(null, ',', '{', '}')"));
+
+        Assert.assertEquals("GET_TAG_ATTR_NAMES()",           "attr1", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_NAMES()"));
+        Assert.assertEquals("GET_TAG_ATTR_NAMES(null)",       "attr1", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_NAMES(null)"));
+        Assert.assertEquals("GET_TAG_ATTR_NAMES(null, '|',)", "attr1", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_NAMES(null, '|')"));
+        Assert.assertEquals("GET_TAG_ATTR_NAMES(null, null)", "attr1", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_NAMES(null, null)"));
+
+        Assert.assertEquals("GET_TAG_ATTR_NAMES_Q()",                    "'attr1'", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_NAMES_Q()"));
+        Assert.assertEquals("GET_TAG_ATTR_NAMES_Q(null)",                "'attr1'", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_NAMES_Q(null)"));
+        Assert.assertEquals("GET_TAG_ATTR_NAMES_Q(null, '|')",           "'attr1'", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_NAMES_Q(null, '|')"));
+        Assert.assertEquals("GET_TAG_ATTR_NAMES_Q(null, null)",          "'attr1'",   evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_NAMES_Q(null, null)"));
+        Assert.assertEquals("GET_TAG_ATTR_NAMES_Q(null, '|', null)",     "attr1",   evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_NAMES_Q(null, '|', null)"));
+        Assert.assertEquals("GET_TAG_ATTR_NAMES_Q(null, ',', '{', '}')", "{attr1}", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_NAMES_Q(null, ',', '{', '}')"));
+
+        Assert.assertEquals("GET_TAG_ATTR('attr1')",            "PCI_value,PII_value", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR('attr1')"));
+        Assert.assertEquals("GET_TAG_ATTR('attr1', null)",      "PCI_value,PII_value", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR('attr1', null)"));
+        Assert.assertEquals("GET_TAG_ATTR('attr1', null, '|')", "PCI_value|PII_value", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR('attr1', null, '|')"));
+        Assert.assertEquals("GET_TAG_ATTR('attr1', null, null)", "PCI_valuePII_value", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR('attr1', null, null)"));
+
+        Assert.assertEquals("GET_TAG_ATTR_Q('attr1')",                      "'PCI_value','PII_value'", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_Q('attr1')"));
+        Assert.assertEquals("GET_TAG_ATTR_Q('attr1', null)",                "'PCI_value','PII_value'", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_Q('attr1', null)"));
+        Assert.assertEquals("GET_TAG_ATTR_Q('attr1', null, null)",          "'PCI_value''PII_value'",  evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_Q('attr1', null, null)"));
+        Assert.assertEquals("GET_TAG_ATTR_Q('attr1', null, '|')",           "'PCI_value'|'PII_value'", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_Q('attr1', null, '|')"));
+        Assert.assertEquals("GET_TAG_ATTR_Q('attr1', null, ',', null)",     "PCI_value,PII_value",     evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_Q('attr1', null, ',', null)"));
+        Assert.assertEquals("GET_TAG_ATTR_Q('attr1', null, ',', '{', '}')", "{PCI_value},{PII_value}", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_Q('attr1', null, ',', '{', '}')"));
+
+        Assert.assertEquals("GET_UG_NAMES()",           "test-group1,test-group2", evaluator.evaluateScript(scriptEngine, "GET_UG_NAMES()"));
+        Assert.assertEquals("GET_UG_NAMES(null)",       "test-group1,test-group2", evaluator.evaluateScript(scriptEngine, "GET_UG_NAMES(null)"));
+        Assert.assertEquals("GET_UG_NAMES(null, '|')",  "test-group1|test-group2", evaluator.evaluateScript(scriptEngine, "GET_UG_NAMES(null, '|')"));
+        Assert.assertEquals("GET_UG_NAMES(null, null)", "test-group1test-group2",  evaluator.evaluateScript(scriptEngine, "GET_UG_NAMES(null, null)"));
+
+        Assert.assertEquals("GET_UG_NAMES_Q()",                    "'test-group1','test-group2'", evaluator.evaluateScript(scriptEngine, "GET_UG_NAMES_Q()"));
+        Assert.assertEquals("GET_UG_NAMES_Q(null)",                "'test-group1','test-group2'", evaluator.evaluateScript(scriptEngine, "GET_UG_NAMES_Q(null)"));
+        Assert.assertEquals("GET_UG_NAMES_Q(null, null)",          "'test-group1''test-group2'", evaluator.evaluateScript(scriptEngine, "GET_UG_NAMES_Q(null, null)"));
+        Assert.assertEquals("GET_UG_NAMES_Q(null, '|')",           "'test-group1'|'test-group2'", evaluator.evaluateScript(scriptEngine, "GET_UG_NAMES_Q(null, '|')"));
+        Assert.assertEquals("GET_UG_NAMES_Q(null, ',', null)",     "test-group1,test-group2",     evaluator.evaluateScript(scriptEngine, "GET_UG_NAMES_Q(null, ',', null)"));
+        Assert.assertEquals("GET_UG_NAMES_Q(null, ',', '{', '}')", "{test-group1},{test-group2}", evaluator.evaluateScript(scriptEngine, "GET_UG_NAMES_Q(null, ',', '{', '}')"));
+
+        Assert.assertEquals("GET_UG_ATTR_NAMES()",           "dept,site", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_NAMES()"));
+        Assert.assertEquals("GET_UG_ATTR_NAMES(null)",       "dept,site", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_NAMES(null)"));
+        Assert.assertEquals("GET_UG_ATTR_NAMES(null, '|')",  "dept|site", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_NAMES(null, '|')"));
+        Assert.assertEquals("GET_UG_ATTR_NAMES(null, null)", "deptsite",  evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_NAMES(null, null)"));
+
+        Assert.assertEquals("GET_UG_ATTR_NAMES_Q()",                    "'dept','site'", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_NAMES_Q()"));
+        Assert.assertEquals("GET_UG_ATTR_NAMES_Q(null)",                "'dept','site'", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_NAMES_Q(null)"));
+        Assert.assertEquals("GET_UG_ATTR_NAMES_Q(null, null)",          "'dept''site'", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_NAMES_Q(null, null)"));
+        Assert.assertEquals("GET_UG_ATTR_NAMES_Q(null, '|')",           "'dept'|'site'", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_NAMES_Q(null, '|')"));
+        Assert.assertEquals("GET_UG_ATTR_NAMES_Q(null, ',', null)",     "dept,site",     evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_NAMES_Q(null, ',', null)"));
+        Assert.assertEquals("GET_UG_ATTR_NAMES_Q(null, ',', '{', '}')", "{dept},{site}", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_NAMES_Q(null, ',', '{', '}')"));
+
+        Assert.assertEquals("GET_UG_ATTR('dept')",             "ENGG,PROD", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR('dept')"));
+        Assert.assertEquals("GET_UG_ATTR('dept', null)",       "ENGG,PROD", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR('dept', null)"));
+        Assert.assertEquals("GET_UG_ATTR('dept', null, '|')",  "ENGG|PROD", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR('dept', null, '|')"));
+        Assert.assertEquals("GET_UG_ATTR('dept', null, null)", "ENGGPROD",  evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR('dept', null, null)"));
+
+        Assert.assertEquals("GET_UG_ATTR_Q('dept')",                      "'ENGG','PROD'", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_Q('dept')"));
+        Assert.assertEquals("GET_UG_ATTR_Q('dept', null)",                "'ENGG','PROD'", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_Q('dept', null)"));
+        Assert.assertEquals("GET_UG_ATTR_Q('dept', null, null)",          "'ENGG''PROD'",  evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_Q('dept', null, null)"));
+        Assert.assertEquals("GET_UG_ATTR_Q('dept', null, '|')",           "'ENGG'|'PROD'", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_Q('dept', null, '|')"));
+        Assert.assertEquals("GET_UG_ATTR_Q('dept', null, ',', null)",     "ENGG,PROD",     evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_Q('dept', null, ',', null)"));
+        Assert.assertEquals("GET_UG_ATTR_Q('dept', null, ',', '{', '}')", "{ENGG},{PROD}", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_Q('dept', null, ',', '{', '}')"));
+
+        Assert.assertEquals("GET_UG_ATTR('site')",             "10,20", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR('site')"));
+        Assert.assertEquals("GET_UG_ATTR('site', null)",       "10,20", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR('site', null)"));
+        Assert.assertEquals("GET_UG_ATTR('site', null, '|')",  "10|20", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR('site', null, '|')"));
+        Assert.assertEquals("GET_UG_ATTR('site', null, null)", "1020",  evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR('site', null, null)"));
+
+        Assert.assertEquals("GET_UG_ATTR_Q('site')",                      "'10','20'", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_Q('site')"));
+        Assert.assertEquals("GET_UG_ATTR_Q('site', null)",                "'10','20'", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_Q('site', null)"));
+        Assert.assertEquals("GET_UG_ATTR_Q('site', null, null)",          "'10''20'",  evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_Q('site', null, null)"));
+        Assert.assertEquals("GET_UG_ATTR_Q('site', null, '|')",           "'10'|'20'", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_Q('site', null, '|')"));
+        Assert.assertEquals("GET_UG_ATTR_Q('site', null, ',', null)",     "10,20",     evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_Q('site', null, ',', null)"));
+        Assert.assertEquals("GET_UG_ATTR_Q('site', null, ',', '{', '}')", "{10},{20}", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_Q('site', null, ',', '{', '}')"));
+
+        Assert.assertEquals("GET_UR_NAMES()",           "test-role1,test-role2", evaluator.evaluateScript(scriptEngine, "GET_UR_NAMES()"));
+        Assert.assertEquals("GET_UR_NAMES(null)",       "test-role1,test-role2", evaluator.evaluateScript(scriptEngine, "GET_UR_NAMES(null)"));
+        Assert.assertEquals("GET_UR_NAMES(null, '|')",  "test-role1|test-role2", evaluator.evaluateScript(scriptEngine, "GET_UR_NAMES(null, '|')"));
+        Assert.assertEquals("GET_UR_NAMES(null, null)", "test-role1test-role2",  evaluator.evaluateScript(scriptEngine, "GET_UR_NAMES(null, null)"));
+
+        Assert.assertEquals("GET_UR_NAMES_Q()",                    "'test-role1','test-role2'", evaluator.evaluateScript(scriptEngine, "GET_UR_NAMES_Q()"));
+        Assert.assertEquals("GET_UR_NAMES_Q(null)",                "'test-role1','test-role2'", evaluator.evaluateScript(scriptEngine, "GET_UR_NAMES_Q(null)"));
+        Assert.assertEquals("GET_UR_NAMES_Q(null, null)",          "'test-role1''test-role2'", evaluator.evaluateScript(scriptEngine, "GET_UR_NAMES_Q(null, null)"));
+        Assert.assertEquals("GET_UR_NAMES_Q(null, '|')",           "'test-role1'|'test-role2'", evaluator.evaluateScript(scriptEngine, "GET_UR_NAMES_Q(null, '|')"));
+        Assert.assertEquals("GET_UR_NAMES_Q(null, ',', null)",     "test-role1,test-role2",     evaluator.evaluateScript(scriptEngine, "GET_UR_NAMES_Q(null, ',', null)"));
+        Assert.assertEquals("GET_UR_NAMES_Q(null, ',', '{', '}')", "{test-role1},{test-role2}", evaluator.evaluateScript(scriptEngine, "GET_UR_NAMES_Q(null, ',', '{', '}')"));
+
+        Assert.assertEquals("GET_USER_ATTR_NAMES()",           "state", evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_NAMES()"));
+        Assert.assertEquals("GET_USER_ATTR_NAMES(null)",       "state", evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_NAMES(null)"));
+        Assert.assertEquals("GET_USER_ATTR_NAMES(null, '|')",  "state", evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_NAMES(null, '|')"));
+        Assert.assertEquals("GET_USER_ATTR_NAMES(null, null)", "state",  evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_NAMES(null, null)"));
+
+        Assert.assertEquals("GET_USER_ATTR_NAMES_Q()",                    "'state'", evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_NAMES_Q()"));
+        Assert.assertEquals("GET_USER_ATTR_NAMES_Q(null)",                "'state'", evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_NAMES_Q(null)"));
+        Assert.assertEquals("GET_USER_ATTR_NAMES_Q(null, null)",          "'state'", evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_NAMES_Q(null, null)"));
+        Assert.assertEquals("GET_USER_ATTR_NAMES_Q(null, '|')",           "'state'", evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_NAMES_Q(null, '|')"));
+        Assert.assertEquals("GET_USER_ATTR_NAMES_Q(null, ',', null)",     "state",   evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_NAMES_Q(null, ',', null)"));
+        Assert.assertEquals("GET_USER_ATTR_NAMES_Q(null, ',', '{', '}')", "{state}", evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_NAMES_Q(null, ',', '{', '}')"));
+
+        Assert.assertEquals("GET_USER_ATTR('state')",             "CA", evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR('state')"));
+        Assert.assertEquals("GET_USER_ATTR('state', null)",       "CA", evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR('state', null)"));
+        Assert.assertEquals("GET_USER_ATTR('state', null, '|')",  "CA", evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR('state', null, '|')"));
+        Assert.assertEquals("GET_USER_ATTR('state', null, null)", "CA",  evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR('state', null, null)"));
+
+        Assert.assertEquals("GET_USER_ATTR_Q('state')",                      "'CA'", evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_Q('state')"));
+        Assert.assertEquals("GET_USER_ATTR_Q('state', null)",                "'CA'", evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_Q('state', null)"));
+        Assert.assertEquals("GET_USER_ATTR_Q('state', null, null)",          "'CA'", evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_Q('state', null, null)"));
+        Assert.assertEquals("GET_USER_ATTR_Q('state', null, '|')",           "'CA'", evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_Q('state', null, '|')"));
+        Assert.assertEquals("GET_USER_ATTR_Q('state', null, ',', null)",     "CA",   evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_Q('state', null, ',', null)"));
+        Assert.assertEquals("GET_USER_ATTR_Q('state', null, ',', '{', '}')", "{CA}", evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_Q('state', null, ',', '{', '}')"));
+    }
+
+    @Test
+    public void testNonExistentValues() {
+        RangerAccessRequest          request   = createRequest("test-user", Collections.emptySet(), Collections.emptySet(), Collections.emptyList());
+        RangerRequestScriptEvaluator evaluator = new RangerRequestScriptEvaluator(request);
+
+        // empty TAG names
+        Assert.assertEquals("GET_TAG_NAMES()",              "",      evaluator.evaluateScript(scriptEngine, "GET_TAG_NAMES()"));
+        Assert.assertEquals("GET_TAG_NAMES(null)",          "",      evaluator.evaluateScript(scriptEngine, "GET_TAG_NAMES(null)"));
+        Assert.assertEquals("GET_TAG_NAMES('empty')",       "empty", evaluator.evaluateScript(scriptEngine, "GET_TAG_NAMES('empty')"));
+        Assert.assertEquals("GET_TAG_NAMES('empty', '|')",  "empty", evaluator.evaluateScript(scriptEngine, "GET_TAG_NAMES('empty', '|')"));
+        Assert.assertEquals("GET_TAG_NAMES('empty', null)", "empty", evaluator.evaluateScript(scriptEngine, "GET_TAG_NAMES('empty', null)"));
+
+        // empty TAG names
+        Assert.assertEquals("GET_TAG_NAMES_Q()",                       "",        evaluator.evaluateScript(scriptEngine, "GET_TAG_NAMES_Q()"));
+        Assert.assertEquals("GET_TAG_NAMES_Q(null)",                   "",        evaluator.evaluateScript(scriptEngine, "GET_TAG_NAMES_Q(null)"));
+        Assert.assertEquals("GET_TAG_NAMES_Q('empty')",                "'empty'", evaluator.evaluateScript(scriptEngine, "GET_TAG_NAMES_Q('empty')"));
+        Assert.assertEquals("GET_TAG_NAMES_Q('empty', ',')",           "'empty'", evaluator.evaluateScript(scriptEngine, "GET_TAG_NAMES_Q('empty', ',')"));
+        Assert.assertEquals("GET_TAG_NAMES_Q('empty', '|', null)",     "'empty'", evaluator.evaluateScript(scriptEngine, "GET_TAG_NAMES_Q('empty', '|')"));
+        Assert.assertEquals("GET_TAG_NAMES_Q('empty', ',', '{', '}')", "{empty}", evaluator.evaluateScript(scriptEngine, "GET_TAG_NAMES_Q('empty', ',', '{', '}')"));
+
+        // empty UG names
+        Assert.assertEquals("GET_UG_NAMES()",              "",      evaluator.evaluateScript(scriptEngine, "GET_UG_NAMES()"));
+        Assert.assertEquals("GET_UG_NAMES(null)",          "",      evaluator.evaluateScript(scriptEngine, "GET_UG_NAMES(null)"));
+        Assert.assertEquals("GET_UG_NAMES('empty')",       "empty", evaluator.evaluateScript(scriptEngine, "GET_UG_NAMES('empty')"));
+        Assert.assertEquals("GET_UG_NAMES('empty', '|')",  "empty", evaluator.evaluateScript(scriptEngine, "GET_UG_NAMES('empty', '|')"));
+        Assert.assertEquals("GET_UG_NAMES('empty', null)", "empty", evaluator.evaluateScript(scriptEngine, "GET_UG_NAMES('empty', null)"));
+
+        // empty UG names
+        Assert.assertEquals("GET_UG_NAMES_Q()",                       "",        evaluator.evaluateScript(scriptEngine, "GET_UG_NAMES_Q()"));
+        Assert.assertEquals("GET_UG_NAMES_Q(null)",                   "",        evaluator.evaluateScript(scriptEngine, "GET_UG_NAMES_Q(null)"));
+        Assert.assertEquals("GET_UG_NAMES_Q('empty')",                "'empty'", evaluator.evaluateScript(scriptEngine, "GET_UG_NAMES_Q('empty')"));
+        Assert.assertEquals("GET_UG_NAMES_Q('empty', ',')",           "'empty'", evaluator.evaluateScript(scriptEngine, "GET_UG_NAMES_Q('empty', ',')"));
+        Assert.assertEquals("GET_UG_NAMES_Q('empty', '|', null)",     "'empty'", evaluator.evaluateScript(scriptEngine, "GET_UG_NAMES_Q('empty', '|')"));
+        Assert.assertEquals("GET_UG_NAMES_Q('empty', ',', '{', '}')", "{empty}", evaluator.evaluateScript(scriptEngine, "GET_UG_NAMES_Q('empty', ',', '{', '}')"));
+
+        // empty UR names
+        Assert.assertEquals("GET_UR_NAMES()",              "",      evaluator.evaluateScript(scriptEngine, "GET_UR_NAMES()"));
+        Assert.assertEquals("GET_UR_NAMES(null)",          "",      evaluator.evaluateScript(scriptEngine, "GET_UR_NAMES(null)"));
+        Assert.assertEquals("GET_UR_NAMES('empty')",       "empty", evaluator.evaluateScript(scriptEngine, "GET_UR_NAMES('empty')"));
+        Assert.assertEquals("GET_UR_NAMES('empty', '|')",  "empty", evaluator.evaluateScript(scriptEngine, "GET_UR_NAMES('empty', '|')"));
+        Assert.assertEquals("GET_UR_NAMES('empty', null)", "empty", evaluator.evaluateScript(scriptEngine, "GET_UR_NAMES('empty', null)"));
+
+        // empty UR names
+        Assert.assertEquals("GET_UR_NAMES_Q()",                       "",        evaluator.evaluateScript(scriptEngine, "GET_UR_NAMES_Q()"));
+        Assert.assertEquals("GET_UR_NAMES_Q(null)",                   "",        evaluator.evaluateScript(scriptEngine, "GET_UR_NAMES_Q(null)"));
+        Assert.assertEquals("GET_UR_NAMES_Q('empty')",                "'empty'", evaluator.evaluateScript(scriptEngine, "GET_UR_NAMES_Q('empty')"));
+        Assert.assertEquals("GET_UR_NAMES_Q('empty', ',')",           "'empty'", evaluator.evaluateScript(scriptEngine, "GET_UR_NAMES_Q('empty', ',')"));
+        Assert.assertEquals("GET_UR_NAMES_Q('empty', '|', null)",     "'empty'", evaluator.evaluateScript(scriptEngine, "GET_UR_NAMES_Q('empty', '|')"));
+        Assert.assertEquals("GET_UR_NAMES_Q('empty', ',', '{', '}')", "{empty}", evaluator.evaluateScript(scriptEngine, "GET_UR_NAMES_Q('empty', ',', '{', '}')"));
+
+        // non-existent attribute
+        Assert.assertEquals("GET_TAG_ATTR('noattr')",                "",      evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR('noattr')"));
+        Assert.assertEquals("GET_TAG_ATTR('noattr', null)",          "",      evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR('noattr', null)"));
+        Assert.assertEquals("GET_TAG_ATTR('noattr', 'empty')",       "empty", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR('noattr', 'empty')"));
+        Assert.assertEquals("GET_TAG_ATTR('noattr', 'empty', '|')",  "empty", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR('noattr', 'empty', '|')"));
+        Assert.assertEquals("GET_TAG_ATTR('noattr', 'empty', null)", "empty", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR('noattr', 'empty', null)"));
+
+        // non-existent attribute
+        Assert.assertEquals("GET_TAG_ATTR_Q('noattr')",                         "",        evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_Q('noattr')"));
+        Assert.assertEquals("GET_TAG_ATTR_Q('noattr', null)",                   "",        evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_Q('noattr', null)"));
+        Assert.assertEquals("GET_TAG_ATTR_Q('noattr', 'empty')",                "'empty'", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_Q('noattr', 'empty')"));
+        Assert.assertEquals("GET_TAG_ATTR_Q('noattr', 'empty', ',')",           "'empty'", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_Q('noattr', 'empty', ',')"));
+        Assert.assertEquals("GET_TAG_ATTR_Q('noattr', 'empty', '|', null)",     "empty",   evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_Q('noattr', 'empty', '|', null)"));
+        Assert.assertEquals("GET_TAG_ATTR_Q('noattr', 'empty', ',', '{', '}')", "{empty}", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_Q('noattr', 'empty', ',', '{', '}')"));
+
+        // non-existent attribute
+        Assert.assertEquals("GET_UG_ATTR('noattr')",             "", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR('noattr')"));
+        Assert.assertEquals("GET_UG_ATTR('noattr', null)",       "", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR('noattr', null)"));
+        Assert.assertEquals("GET_UG_ATTR('noattr', 'empty', '|')",  "empty", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR('noattr', 'empty', '|')"));
+        Assert.assertEquals("GET_UG_ATTR('noattr', 'empty', null)", "empty",  evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR('noattr', 'empty', null)"));
+
+        // non-existent attribute
+        Assert.assertEquals("GET_UG_ATTR_Q('noattr')",                         "",        evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_Q('noattr')"));
+        Assert.assertEquals("GET_UG_ATTR_Q('noattr', null)",                   "",        evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_Q('noattr', null)"));
+        Assert.assertEquals("GET_UG_ATTR_Q('noattr', 'empty', null)",          "'empty'", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_Q('noattr', 'empty', null)"));
+        Assert.assertEquals("GET_UG_ATTR_Q('noattr', 'empty', '|')",           "'empty'", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_Q('noattr', 'empty', '|')"));
+        Assert.assertEquals("GET_UG_ATTR_Q('noattr', 'empty', ',', null)",     "empty",   evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_Q('noattr', 'empty', ',', null)"));
+        Assert.assertEquals("GET_UG_ATTR_Q('noattr', 'empty', ',', '{', '}')", "{empty}", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_Q('noattr', 'empty', ',', '{', '}')"));
+
+        // non-existent attribute
+        Assert.assertEquals("GET_USER_ATTR('noattr')",                "",      evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR('noattr')"));
+        Assert.assertEquals("GET_USER_ATTR('noattr', null)",          "",      evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR('noattr', null)"));
+        Assert.assertEquals("GET_USER_ATTR('noattr', 'empty', '|')",  "empty", evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR('noattr', 'empty', '|')"));
+        Assert.assertEquals("GET_USER_ATTR('noattr', 'empty', null)", "empty", evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR('noattr', 'empty', null)"));
+
+        // non-existent attribute
+        Assert.assertEquals("GET_USER_ATTR_Q('noattr')",                         "",        evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_Q('noattr')"));
+        Assert.assertEquals("GET_USER_ATTR_Q('noattr', null)",                   "",        evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_Q('noattr', null)"));
+        Assert.assertEquals("GET_USER_ATTR_Q('noattr', 'empty', null)",          "'empty'", evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_Q('noattr', 'empty', null)"));
+        Assert.assertEquals("GET_USER_ATTR_Q('noattr', 'empty', '|')",           "'empty'", evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_Q('noattr', 'empty', '|')"));
+        Assert.assertEquals("GET_USER_ATTR_Q('noattr', 'empty', ',', null)",     "empty",   evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_Q('noattr', 'empty', ',', null)"));
+        Assert.assertEquals("GET_USER_ATTR_Q('noattr', 'empty', ',', '{', '}')", "{empty}", evaluator.evaluateScript(scriptEngine, "GET_USER_ATTR_Q('noattr', 'empty', ',', '{', '}')"));
     }
 
     @Test