Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for inline style css functions #511

Merged
merged 3 commits into from
Oct 10, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
added support for nested functions and fallback support
garg23neha committed Sep 30, 2024
commit 0b051593939867040cb68cccd9031b16e0923cab
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
@@ -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(");
@@ -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:
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));
}
}