Skip to content

Commit

Permalink
NEW (pmd): @W-17310954@ Adding in some more AppExchange Security rules
Browse files Browse the repository at this point in the history
  • Loading branch information
stephen-carter-at-sf committed Jan 9, 2025
1 parent b3debb0 commit 851adf5
Show file tree
Hide file tree
Showing 23 changed files with 621 additions and 85 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.salesforce.security.pmd.html;

import java.util.List;

import net.sourceforge.pmd.lang.html.ast.ASTHtmlElement;
import net.sourceforge.pmd.lang.html.rule.AbstractHtmlRule;
import net.sourceforge.pmd.lang.rule.xpath.Attribute;

import com.salesforce.security.pmd.utils.SecretsInPackageUtils;

public class DetectHardCodedCredentialsInAura extends AbstractHtmlRule {
private static final String AURA_C_COMPONENT = "C:";
private static final String AURA_HARDCODED_SECRET_VIOLATION =
"Detected a potential hard coded secret in aura:attribute \"%s\"";

@Override
@SuppressWarnings("unchecked")
public Object visit(ASTHtmlElement node, Object data) {
detectHardCodedSecrets(node,data);
return super.visit(node, data);
}

private void detectHardCodedSecrets(ASTHtmlElement htmlElement, Object data) {
String name=htmlElement.getNodeName();
if (name.equalsIgnoreCase("aura:attribute")) {
detectSecretsInAuraAttrs(htmlElement, data);
} else if (name.toUpperCase().startsWith(AURA_C_COMPONENT)) {
detectSecretsInAuraComponent(htmlElement, data);
}

}

private void detectSecretsInAuraComponent(ASTHtmlElement component, Object data) {
List<Attribute> allAttrs=component.getAttributes();
for (Attribute eachAttr: allAttrs) {
String attrName = eachAttr.getName();
if (SecretsInPackageUtils.isAPotentialSecret(attrName)) {
this.asCtx(data).addViolationWithMessage(component,
String.format(AURA_HARDCODED_SECRET_VIOLATION, attrName));
}
}

}

private void detectSecretsInAuraAttrs(ASTHtmlElement nextAttr, Object data) {
String attrName = nextAttr.getAttribute("name");
String type = nextAttr.getAttribute("type");
if (type!=null && //handles null pointer exception when type is not specified
(type.compareToIgnoreCase("string")!=0 && type.compareToIgnoreCase("list")!=0)) {
return;
}
String defaultValue = nextAttr.getAttribute("default");
if (defaultValue==null || defaultValue.isEmpty()){
return;
}
if (SecretsInPackageUtils.isAPotentialSecret(attrName)) {
this.asCtx(data).addViolationWithMessage(nextAttr,
String.format(AURA_HARDCODED_SECRET_VIOLATION, attrName));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.salesforce.security.pmd.html;

import net.sourceforge.pmd.lang.html.ast.ASTHtmlElement;
import net.sourceforge.pmd.lang.html.rule.AbstractHtmlRule;

public class DetectUnescapedHtmlInAura extends AbstractHtmlRule {
@Override
@SuppressWarnings("unchecked")
public Object visit(ASTHtmlElement node, Object data) {
if (node.getNodeName().equalsIgnoreCase("aura:unescapedHtml")) {
this.asCtx(data).addViolation(node); // Message is defined in sfca/rulesets/AppExchange_html.xml file
}
return super.visit(node, data);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package com.salesforce.security.pmd.utils;

import java.util.List;

public class SecretsInPackageUtils {
private static final List<String> PRIVACY_FIELD_MAPPINGS_LIST = List.of(
"SSN",
"SOCIALSECURITY",
"SOCIAL_SECURITY",
"NATIONALID",
"NATIONAL_ID",
"NATIONAL_IDENTIFIER",
"NATIONALIDENTIFIER",
"DRIVERSLICENSE",
"DRIVERS_LICENSE",
"DRIVER_LICENSE",
"DRIVERLICENSE",
"PASSPORT",
"AADHAAR",
"AADHAR" //More?
);

private static final List<String> AUTH_FIELD_MAPPINGS_LIST = List.of(
"KEY", // potentially high false +ve rate
"ACCESS",
"PASS",
"ENCRYPT",
"TOKEN",
"HASH",
"SECRET",
"SIGNATURE",
"SIGN",
"AUTH", //AUTHORIZATION,AUTHENTICATION,AUTHENTICATE,OAUTH
"AUTHORIZATION",
"AUTHENTICATION",
"AUTHENTICATE",
"BEARER",
"CRED", //cred, credential(s),
"REFRESH", //
"CERT",
"PRIVATE",
"PUBLIC",
"JWT"
);

private static final List<String> POTENTIAL_SECERT_VARS_LIST = List.of(
"APIKEY",
"API_KEY",
"API-KEY",
"PASSWORD",
"PASSWD",
"ENCRYPT",
"TOKEN",
"HASH",
"SECRET",
"SIGNATURE",
"AUTHN",
"AUTHZ",
"OAUTH",
"AUTHORIZATION",
"AUTHENTICATION",
"AUTHENTICATE",
"BEARER",
"CREDS", //cred - has too many false +ve hits, credential(s),
"CREDENTIAL",
"REFRESHTOKEN",
"REFRESH_TOKEN",
"CERT",
"PRIVATE",
"SYMMETRICKEY",
"SYMMETRIC_KEY",
"ASYMMETRIC_KEY",
"ASYMMETRICKEY",
"JWT",
"SALT",
"COOKIE",
"SESSIONID",
"SESSION_ID",
"CREDITCARD",
"CREDIT_CARD"
);

private static boolean isAPartialMatchInList(String inputStr, List<String> listOfStrings) {
String inputStrUpper = inputStr.toUpperCase();
for (String eachStr : listOfStrings) {
if (inputStrUpper.contains(eachStr)) {
return true;
}
}
return false;
}

public static boolean isAnAuthTokenField(String fieldName) {
return isAPartialMatchInList(fieldName.toUpperCase(), AUTH_FIELD_MAPPINGS_LIST);
}

public static boolean isAnInsecurePrivacyField(String fieldName) {
return isAPartialMatchInList(fieldName.toUpperCase(), PRIVACY_FIELD_MAPPINGS_LIST);
}

public static boolean isAPotentialSecret(String attrName) {
return isAPartialMatchInList(attrName, POTENTIAL_SECERT_VARS_LIST );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import net.sourceforge.pmd.lang.xml.ast.internal.XmlParserImpl.RootXmlNode;
import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
Expand All @@ -20,48 +19,10 @@
import net.sourceforge.pmd.lang.rule.AbstractRule;
import org.xml.sax.SAXException;

import com.salesforce.security.pmd.utils.SecretsInPackageUtils;

public class DetectSecretsInCustomObjects extends AbstractRule {
private static final List<String> PRIVACY_FIELD_MAPPINGS_LIST = List.of(
"SSN",
"SOCIALSECURITY",
"SOCIAL_SECURITY",
"NATIONALID",
"NATIONAL_ID",
"NATIONAL_IDENTIFIER",
"NATIONALIDENTIFIER",
"DRIVERSLICENSE",
"DRIVERS_LICENSE",
"DRIVER_LICENSE",
"DRIVERLICENSE",
"PASSPORT",
"AADHAAR",
"AADHAR" //More?
);

private static final List<String> AUTH_FIELD_MAPPINGS_LIST = List.of(
"KEY", // potentially high false +ve rate
"ACCESS",
"PASS",
"ENCRYPT",
"TOKEN",
"HASH",
"SECRET",
"SIGNATURE",
"SIGN",
"AUTH", //AUTHORIZATION,AUTHENTICATION,AUTHENTICATE,OAUTH
"AUTHORIZATION",
"AUTHENTICATION",
"AUTHENTICATE",
"BEARER",
"CRED", //cred, credential(s),
"REFRESH", //
"CERT",
"PRIVATE",
"PUBLIC",
"JWT"
);

public static final String VISIBILITY_XPATH_EXPR = "/CustomObject/visibility[text()=\"Public\"]";
public static final String VISIBILITY_XPATH_EXPR = "/CustomObject/visibility[text()=\"Public\"]";
public static final String PRIVACY_FIELD_XPATH_EXPR = "/CustomField/type[text()!=\"EncryptedText\"]";
private static final String CUSTOM_SETTINGS_XPATH_EXPR = "/CustomObject/customSettingsType";

Expand All @@ -73,10 +34,10 @@ public void apply(Node target, RuleContext ctx) {
return;
}
String fieldName = fieldNameFromFileName(fieldFileName);
if (isAnAuthTokenField(fieldName)) {
if (SecretsInPackageUtils.isAnAuthTokenField(fieldName)) {
doObjectVisibilityCheck(ctx, target, fieldFileName);
}
if (isAnInsecurePrivacyField(fieldName)) {
if (SecretsInPackageUtils.isAnInsecurePrivacyField(fieldName)) {
checkFieldType(ctx, target, fieldName);
}
}
Expand Down Expand Up @@ -157,25 +118,6 @@ private String getObjectFileName(String fieldFileName) {
}
}

public boolean isAnAuthTokenField(String fieldName) {
return isAPartialMatchInList(fieldName.toUpperCase(), AUTH_FIELD_MAPPINGS_LIST);
}

public boolean isAnInsecurePrivacyField(String fieldName) {
return isAPartialMatchInList(fieldName.toUpperCase(), PRIVACY_FIELD_MAPPINGS_LIST);
}


private static boolean isAPartialMatchInList(String inputStr, List<String> listOfStrings) {
String inputStrUpper = inputStr.toUpperCase();
for (String eachStr : listOfStrings) {
if (inputStrUpper.contains(eachStr)) {
return true;
}
}
return false;
}

private static Document parseDocument(String xmlFile) throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setExpandEntityReferences(false);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="AppExchange_html"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
<description>AppExchange Security Rules for HTML Language</description>


<rule name="AvoidHardCodedCredentialsInAura"
language="html"
class="com.salesforce.security.pmd.html.DetectHardCodedCredentialsInAura"
message="Detected use of hard coded credentials in Aura component"
externalInfoUrl="https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/guide/rules-pmd-appexchange.html#avoidhardcodedcredentialsinaura">
<description>Detects use of hard coded credentials in Aura components.</description>
<priority>2</priority>
</rule>


<rule name="AvoidUnescapedHtmlInAura"
language="html"
class="com.salesforce.security.pmd.html.DetectUnescapedHtmlInAura"
message="Detected use of aura:unescapedHtml"
externalInfoUrl="https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/guide/rules-pmd-appexchange.html#avoidunescapedhtmlinaura">
<description>Detects use of aura:unescapedHtml,which should be used cautiously. Developers should ensure that the unescapedHtml should not use tainted input to protect against XSS.</description>
<priority>2</priority>
</rule>


<rule name="DetectUseLwcDomManual"
language="html"
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"
message="Protect against XSS when using lwc:dom=&quot;manual&quot;."
externalInfoUrl="https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/guide/rules-pmd-appexchange.html#detectuselwcdommanual">
<description>Detects instances of lwc:dom=&quot;manual&quot; that could allow unintentional or malicious user input. Don't allow user input on these elements.</description>
<priority>3</priority>
<properties>
<property name="xpath">
<value><![CDATA[
//*[@*[local-name()="lwc:dom" and .="manual"]]
]]></value>
</property>
</properties>
</rule>

</ruleset>
Loading

0 comments on commit 851adf5

Please sign in to comment.