Skip to content

Commit

Permalink
added support for nested functions and fallback support
Browse files Browse the repository at this point in the history
  • Loading branch information
garg23neha committed Sep 30, 2024
1 parent 91a64a6 commit 0b05159
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 50 deletions.
38 changes: 31 additions & 7 deletions src/main/java/org/owasp/validator/css/CssValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -333,13 +333,20 @@ public String lexicalValueToString(LexicalUnit lu) {
return String.valueOf(lu.getFloatValue());
case LexicalUnit.SAC_STRING_VALUE:
case LexicalUnit.SAC_IDENT:
// just a string/identifier
// Ensure that JavaScript URLs are not allowed
String stringValue = lu.getStringValue();
if (stringValue == null || stringValue.toLowerCase().startsWith("javascript:")) {
return null;
}
if (stringValue.indexOf(" ") != -1) stringValue = "'" + stringValue + "'";
return stringValue;
case LexicalUnit.SAC_URI:
// this is a URL
return "url(" + lu.getStringValue() + ")";
// Ensure that JavaScript URLs are not allowed
String url = lu.getStringValue();
if (url == null || url.toLowerCase().startsWith("javascript:")) {
return null;
}
return "url(" + url + ")";
case LexicalUnit.SAC_RGBCOLOR:
// this is a rgb encoded color
StringBuffer sb = new StringBuffer("rgb(");
Expand All @@ -362,17 +369,34 @@ public String lexicalValueToString(LexicalUnit lu) {
case LexicalUnit.SAC_OPERATOR_COMMA:
return ",";
case LexicalUnit.SAC_FUNCTION:
final StringBuffer builder = new StringBuffer();
builder.append(lu.getFunctionName());
builder.append("(");
StringBuilder builder = new StringBuilder();

// Append the function name, e.g., "var"
builder.append(lu.getFunctionName()).append("(");

LexicalUnit params = lu.getParameters();
while (params != null) {
builder.append(lexicalValueToString(params));
String paramsValue = lexicalValueToString(params);
if (paramsValue == null) {
return null;
}
builder.append(paramsValue);
params = params.getNextLexicalUnit();
if (params != null) {
builder.append(", ");
}
}

// Check for fallback (some functions like "var" have fallback values)
LexicalUnit fallback = lu.getPreviousLexicalUnit();
if (fallback != null) {
String fallbackValue = lexicalValueToString(fallback);
if (fallbackValue == null) {
return null;
}
builder.append(", ").append(fallbackValue);
}

builder.append(")");
return builder.toString();
case LexicalUnit.SAC_ATTR:
Expand Down
177 changes: 134 additions & 43 deletions src/test/java/org/owasp/validator/css/CssValidatorTest.java
Original file line number Diff line number Diff line change
@@ -1,51 +1,142 @@
package org.owasp.validator.css;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import org.apache.batik.css.parser.CSSLexicalUnit;
import org.junit.Test;
import org.w3c.css.sac.LexicalUnit;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

public class CssValidatorTest {
@Test
public void testLexicalValueToStringSacFunction() {
CssValidator cssValidator = new CssValidator(null);

final CSSLexicalUnit param =
CSSLexicalUnit.createString(LexicalUnit.SAC_STRING_VALUE, "--ds-text-purple", null);
final CSSLexicalUnit varFunc = CSSLexicalUnit.createFunction("var", param, null);

assertEquals("var(--ds-text-purple)", cssValidator.lexicalValueToString(varFunc));

final CSSLexicalUnit hslaParam = CSSLexicalUnit.createInteger(100, null);
final CSSLexicalUnit hslaParam1 = CSSLexicalUnit.createDimension(98, "%", hslaParam);
final CSSLexicalUnit hslaParam2 = CSSLexicalUnit.createDimension(50, "%", hslaParam1);
CSSLexicalUnit.createFloat(LexicalUnit.SAC_REAL, 0.3f, hslaParam2);

final CSSLexicalUnit hslaFunc = CSSLexicalUnit.createFunction("hsla", hslaParam, null);
assertEquals("hsla(100, 98.0%, 50.0%, 0.3)", cssValidator.lexicalValueToString(hslaFunc));
}

@Test
public void testLexicalValueToStringSacFunctionTwoParams() {
CssValidator cssValidator = new CssValidator(null);

final CSSLexicalUnit param =
CSSLexicalUnit.createString(LexicalUnit.SAC_STRING_VALUE, "--ds-text-purple", null);
CSSLexicalUnit.createString(LexicalUnit.SAC_STRING_VALUE, "#FFFFFF", param);
final CSSLexicalUnit func = CSSLexicalUnit.createFunction("var", param, null);

assertEquals("var(--ds-text-purple, #FFFFFF)", cssValidator.lexicalValueToString(func));
}

@Test
public void testLexicalValueToStringUnsupported() {
CssValidator cssValidator = new CssValidator(null);
final CSSLexicalUnit param =
CSSLexicalUnit.createString(LexicalUnit.SAC_STRING_VALUE, "section", null);
final CSSLexicalUnit func =
CSSLexicalUnit.createPredefinedFunction(LexicalUnit.SAC_COUNTER_FUNCTION, param, null);
assertNull(cssValidator.lexicalValueToString(func));
}
@Test
public void testLexicalValueToStringSacFunction() {
CssValidator cssValidator = new CssValidator(null);

final CSSLexicalUnit param =
CSSLexicalUnit.createString(LexicalUnit.SAC_STRING_VALUE, "--ds-text-purple", null);
final CSSLexicalUnit varFunc = CSSLexicalUnit.createFunction("var", param, null);

assertEquals("var(--ds-text-purple)", cssValidator.lexicalValueToString(varFunc));

final CSSLexicalUnit hslaParam = CSSLexicalUnit.createInteger(100, null);
final CSSLexicalUnit hslaParam1 = CSSLexicalUnit.createDimension(98, "%", hslaParam);
final CSSLexicalUnit hslaParam2 = CSSLexicalUnit.createDimension(50, "%", hslaParam1);
CSSLexicalUnit.createFloat(LexicalUnit.SAC_REAL, 0.3f, hslaParam2);

final CSSLexicalUnit hslaFunc = CSSLexicalUnit.createFunction("hsla", hslaParam, null);
assertEquals("hsla(100, 98.0%, 50.0%, 0.3)", cssValidator.lexicalValueToString(hslaFunc));
}

@Test
public void testLexicalValueToStringSacFunctionTwoParams() {
CssValidator cssValidator = new CssValidator(null);

final CSSLexicalUnit param =
CSSLexicalUnit.createString(LexicalUnit.SAC_STRING_VALUE, "--ds-text-purple", null);
CSSLexicalUnit.createString(LexicalUnit.SAC_STRING_VALUE, "#FFFFFF", param);
final CSSLexicalUnit func = CSSLexicalUnit.createFunction("var", param, null);

assertEquals("var(--ds-text-purple, #FFFFFF)", cssValidator.lexicalValueToString(func));
}

@Test
public void testLexicalValueToStringUnsupported() {
CssValidator cssValidator = new CssValidator(null);
final CSSLexicalUnit param =
CSSLexicalUnit.createString(LexicalUnit.SAC_STRING_VALUE, "section", null);
final CSSLexicalUnit func =
CSSLexicalUnit.createPredefinedFunction(LexicalUnit.SAC_COUNTER_FUNCTION, param, null);
assertNull(cssValidator.lexicalValueToString(func));
}

@Test
public void testLexicalValueToStringNestedVarsWithFallback() {
CssValidator cssValidator = new CssValidator(null);

// Create fallback first: --ds-text-purple, #FFFFFF
final CSSLexicalUnit param =
CSSLexicalUnit.createString(LexicalUnit.SAC_STRING_VALUE, "--ds-text-purple", null);
final CSSLexicalUnit fallback =
CSSLexicalUnit.createString(LexicalUnit.SAC_STRING_VALUE, "#FFFFFF", null);

// Create first var() function with fallback
final CSSLexicalUnit function = CSSLexicalUnit.createFunction("var", param, fallback);

// Check if the output is as expected for first var()
assertEquals("var(--ds-text-purple, #FFFFFF)", cssValidator.lexicalValueToString(function));

// Create outer variable: --custom-prop
final CSSLexicalUnit outerParam =
CSSLexicalUnit.createString(LexicalUnit.SAC_STRING_VALUE, "--custom-prop", null);

// Create outer var() with the first function as fallback
final CSSLexicalUnit outerFunction = CSSLexicalUnit.createFunction("var", outerParam, function);

// Ensure the output is as expected for the nested var() function
assertEquals(
"var(--custom-prop, var(--ds-text-purple, #FFFFFF))",
cssValidator.lexicalValueToString(outerFunction));
}

@Test
public void testDefaultPolicyUrlFunction() {
CssValidator cssValidator = new CssValidator(null);

// Test a simple url function
final CSSLexicalUnit urlParam =
CSSLexicalUnit.createString(LexicalUnit.SAC_STRING_VALUE, "http://example.com", null);
final CSSLexicalUnit urlFunc = CSSLexicalUnit.createFunction("url", urlParam, null);

assertEquals("url(http://example.com)", cssValidator.lexicalValueToString(urlFunc));
}

@Test
public void testDefaultPolicyUrlFunctionWithJavaScript() {
CssValidator cssValidator = new CssValidator(null);

// Test a url function with a JavaScript URL
final CSSLexicalUnit urlParam =
CSSLexicalUnit.createString(LexicalUnit.SAC_STRING_VALUE, "javascript:alert(1)", null);
final CSSLexicalUnit urlFunc = CSSLexicalUnit.createFunction("url", urlParam, null);

// Ensure that JavaScript URLs are not allowed
assertNull(cssValidator.lexicalValueToString(urlFunc));
}

@Test
public void testLexicalValueToStringNestedVarsWithJavaScriptAsFallback() {
CssValidator cssValidator = new CssValidator(null);

// Create fallback first: --ds-text-purple, #FFFFFF
final CSSLexicalUnit param =
CSSLexicalUnit.createString(LexicalUnit.SAC_STRING_VALUE, "--custom-url", null);
final CSSLexicalUnit fallback =
CSSLexicalUnit.createString(LexicalUnit.SAC_STRING_VALUE, "javascript:alert(1)", null);

// Create first var() function with fallback
final CSSLexicalUnit function = CSSLexicalUnit.createFunction("var", param, fallback);

// Check if the output is as expected for first var()
assertNull(cssValidator.lexicalValueToString(function));
}

@Test
public void testSacUriWithJavaScriptUrl() {
CssValidator cssValidator = new CssValidator(null);

// Test with a JavaScript URL, which should be blocked
final CSSLexicalUnit jsUrl =
CSSLexicalUnit.createString(LexicalUnit.SAC_URI, "javascript:alert(1)", null);
assertNull("JavaScript URL should be blocked", cssValidator.lexicalValueToString(jsUrl));
}

@Test
public void testSacUriWithValidUrl() {
CssValidator cssValidator = new CssValidator(null);

// Test with a valid URL, which should be allowed
final CSSLexicalUnit validUrl =
CSSLexicalUnit.createString(LexicalUnit.SAC_URI, "https://example.com", null);
assertEquals("url(https://example.com)", cssValidator.lexicalValueToString(validUrl));
}
}

0 comments on commit 0b05159

Please sign in to comment.