Visualforce: pages, components, and controllers
Vulkro checks every *.page and *.component file and every Apex controller bound to one. The findings cover cross-site scripting, untrusted script and iframe sources, and actions that run in system context.
vulkro-sf scan ./force-app
XSS through escape="false"
<apex:outputText escape="false" value="{!$CurrentPage.parameters.q}" />
Turning off the default encoding brings back reflected XSS. High when the value is a request parameter ($CurrentPage.parameters.*), Medium when it is a controller property (stored XSS). Values wrapped in HTMLENCODE(), JSENCODE() or JSINHTMLENCODE() are not flagged.
Merge fields inside <script>
<script>
var term = '{!$CurrentPage.parameters.term}';
</script>
Merge values in a script block are not JavaScript-escaped, so a quote or </script> breaks out. High when the value comes from the request. Values from stored data are reported at Low confidence, which the default view hides; pass --min-confidence low to see them.
Dynamic <apex:includeScript> and <apex:iframe>
<apex:includeScript value="{!scriptUrl}" />
<apex:iframe src="{!$CurrentPage.parameters.next}" />
A script source from a merge field lets an attacker load any JavaScript (CWE-829). An iframe source from the request is an open redirect and phishing surface (CWE-601). $Resource.* references are static and skipped. A URLENCODE(...) value in an iframe is reported at Low confidence (still an open redirect).
Tainted controller properties
public class SearchCtrl {
public String query {
get { return ApexPages.currentPage().getParameters().get('q'); }
set;
}
}
A property that returns a request parameter without escaping taints every page that renders it. Medium when the controller is with sharing, High when it has no sharing keyword. Fix: escape at the controller with String.escapeHtml4(...) or check against an allowlist.
<apex:commandButton> on a controller without sharing
<apex:page controller="ReportCtrl">
<apex:form>
<apex:commandButton action="{!run}" value="Run report" />
</apex:form>
</apex:page>
If ReportCtrl declares without sharing or no sharing keyword, its actions run in system context for every visitor and its DML ignores sharing rules. Reported at Medium, High confidence. Fix: declare with sharing or inherited sharing; move any action that truly needs system context into a small helper class with an explicit ownership and FLS check.
Not covered yet
- Property flow from one custom component (
<c:custom>) to another. - Trusted Site URLs, which are org settings rather than page code.
LWC and Aura have their own checks: see LWC and Aura.
See also: Apex detectors, AgentExchange Top-20 coverage