Visualforce: pages, components, and controllers
Vulkro scans every *.page and *.component file under your project,
and every Apex controller class (*.cls) that is bound to one of those
pages. Findings cluster around three classes of issue: reflected and
stored cross-site scripting, open-redirect / iframe phishing, and
sharing-rule bypass through <apex:commandButton> actions.
Reflected XSS via escape="false"
<apex:outputText escape="false" value="{!$CurrentPage.parameters.q}" />
Visualforce's default merge-field encoding is HTML-safe; opting out with
escape="false" reintroduces the entire reflected-XSS surface. Vulkro
emits at High severity when the merge expression is
$CurrentPage.parameters.* (directly attacker-controllable) and at
Medium when it is a controller property (stored XSS shape).
The rule skips merge expressions wrapped in HTMLENCODE(), JSENCODE(),
or JSINHTMLENCODE(): those are escaped for their context and are
safe even with escape="false".
Reflected XSS inside <script> blocks
Apex merge expressions inside <script> bodies are NOT JavaScript-
escaped by the platform. A value containing a quote or </script>
breaks out of the JS context.
<script>
var term = '{!$CurrentPage.parameters.term}';
doSearch(term);
</script>
Vulkro emits at High when the merge value is request-derived. Stored
inputs emit at Low confidence because we cannot prove the value flows
from an attacker-controlled source without dataflow analysis: surface
them with vulkro scan --all-confidence.
Dynamic <apex:includeScript> and <apex:iframe>
<apex:includeScript value="{!scriptUrl}" />
<apex:iframe src="{!$CurrentPage.parameters.next}" />
A merge-field-derived script source lets an attacker load arbitrary JavaScript into the page (CWE-829). An iframe with a request-derived src is the canonical open-redirect / phishing surface on a Visualforce page (CWE-601).
For both rules, $Resource.* references are static and safe (skipped).
The iframe rule also distinguishes URLENCODE(...) -wrapped values
(percent-encoded, XSS-safe but still open-redirect: Low confidence)
from raw merge expressions (Medium or High depending on the source).
Controller taint sources
The back-end half of a reflected-XSS pair is a controller property
that pulls its value from
ApexPages.currentPage().getParameters().get(...) without an escape
helper. Any Visualforce page that binds this controller and renders
the property inherits the taint.
public class SearchCtrl {
public String query {
get { return ApexPages.currentPage().getParameters().get('q'); }
set;
}
}
Vulkro flags the property at Medium severity when the controller is
with sharing and High when the controller has no sharing keyword (the
property's lifecycle and the sharing posture both feed the visiting
user's session, so the combination changes the blast radius).
Wrap the request-parameter read in String.escapeHtml4(...) (or an
allow-list) at the controller level. The platform's automatic
merge-field encoding does not re-encode an already-tainted property.
<apex:commandButton> on a non-with sharing controller
A button action wired to a controller declared without sharing (or
with no sharing keyword) runs in system context for every page visitor.
Any DML inside that action bypasses the org's sharing rules.
<apex:page controller="ReportCtrl">
<apex:form>
<apex:commandButton action="{!run}" value="Run report" />
</apex:form>
</apex:page>
public class ReportCtrl {
public PageReference run() {
update [SELECT Id FROM Lead WHERE OwnerId = :userInput];
return null;
}
}
Vulkro emits at Medium with High confidence when the page-bound
controller has no with sharing / inherited sharing keyword. Add
the keyword to the controller declaration; if a specific action
genuinely needs system context, isolate it in a small helper class,
document the reason, and add a record-ownership / FLS check before
the DML.
What is not covered yet
- Composite component bindings (
<c:custom>) are walked but the rule does not yet follow component-to-component property flow. - Lightning Web Security (LWS) policies are out of scope for the
Visualforce rules; LWC-specific checks live in
vulkroLWC detection. - Trusted Site URL validation is configured at the org level and is outside Vulkro's static-analysis surface.