forcedotcom / forcedotcom/code-analyzer
[BUG][code-analyzer] sfge: indexing a value the engine cannot resolve aborts the entry point instead of degrading
- Dominant language
- TypeScript
- Stars
- 240
- Forks
- 52
- Avg merge
- 1d 23h
- Merged PRs (30d)
- 5
Description
### Have you tried to resolve this issue yourself first?
- [x] I confirm I have gone through the above steps and still have an issue to report.
### Bug Description
**Engine:** `sfge` (Salesforce Graph Engine) · **Rule:** `ApexFlsViolation` (DevPreview) · **Selector:** `--rule-selector sfge`
An array/index load whose target resolves to an indeterminate `ApexSingleValue` aborts path evaluation — in a method that already returns an indeterminate value for every other input.
```apex
List parts = (List) byKey.get('k'); // byKey is Map
String first = parts[0];
```
`ApexMapValue.apply` takes the indeterminate branch of `METHOD_GET` (`ApexMapValue.java:183-191`) and returns a bare indeterminate value typed from the map's declared value type. Because that type is `Object`, `build()` yields an `ApexSingleValue` rather than an `ApexListValue`, and indexing it hits the unhandled `else` at `PathScopeVisitor.java:894`. The Apex cast to `List` does not help — the engine keeps the map's declared value type. `JSONDeserializeFactory.java:78-79` builds the same shape for types it declines to model, so this is not the only route in.
**The fix already exists in the same method:**
```java
// PathScopeVisitor.java:874-902
private ApexValue getIndeterminantArrayLoadValue(@Nullable ApexValue apexValue) {
final Typeable listType;
if (apexValue instanceof ApexListValue) { ...
} else if (apexValue instanceof ApexForLoopValue) { ...
} else if (apexValue instanceof ApexSoqlValue) { ...
} else if (apexValue == null) {
listType = null; // <-- null is fine
} else {
throw new UnexpectedException(apexValue); // <-- everything else aborts
}
ApexValueBuilder builder = ApexValueBuilder.get(this).withStatus(ValueStatus.INDETERMINANT);
if (listType != null) {
return builder.declarationVertex(listType).build();
} else {
return builder.buildUnknownType(); // <-- the null branch lands here
}
}
```
A `null` `apexValue` — strictly *less* information than an indeterminate `ApexSingleValue` — falls through to `buildUnknownType()`.
### Output / Logs
```shell
UnexpectedException: ApexValue(ApexSingleValue) {status=INDETERMINANT, declarationVertex=SyntheticTypedVertex@...,
valueVertex=null, returnedFrom=ApexValue(ApexMapValue) {status=INDETERMINANT,
declarationVertex=Parameter{... Type=Map ... Name=byKey}} ...}
at com.salesforce.graph.symbols.PathScopeVisitor.getIndeterminantArrayLoadValue(PathScopeVisitor.java:894)
at com.salesforce.graph.symbols.PathScopeVisitor.afterVisit(PathScopeVisitor.java:810)
at com.salesforce.graph.symbols.DefaultSymbolProviderVertexVisitor.afterVisit(DefaultSymbolProviderVertexVisitor.java:737)
at com.salesforce.graph.vertex.ArrayLoadExpressionVertex.afterVisit(ArrayLoadExpressionVertex.java:58)
```
### Steps To Reproduce
1. Create an empty SFDX project (`sfdx-project.json` with a single `force-app` package directory).
2. Add `force-app/main/default/classes/IndeterminateMapGetIndex.cls` with the class shown below, plus a standard `IndeterminateMapGetIndex.cls-meta.xml` (apiVersion 62.0).
3. Add `code-analyzer.yml`:
```yaml
engines:
sfge:
java_thread_timeout: 900000
java_thread_count: 4
```
4. Run:
```
sf code-analyzer run --rule-selector sfge --workspace . --config-file code-analyzer.yml
```
5. The run reports an `InternalExecutionError` for the entry point instead of analysing it. That entry point yields no `ApexFlsViolation` findings at all, and nothing in the summary indicates coverage was lost.
```apex
public with sharing class IndeterminateMapGetIndex {
@AuraEnabled
public static void run(Map byKey) {
List parts = (List) byKey.get('k');
String first = parts[0];
Account a = new Account();
a.Name = first;
insert a;
}
}
```
### Expected Behavior
Routing the `else` to `listType = null` would make the unknown case behave like the already-unknown case, which is what the method's name and Javadoc promise ("Generates a single indeterminant value based on the type contained in `apexValue`").
The caller has the same gap: `PathScopeVisitor.java:804-806` carries a matching bare `throw new UnexpectedException(vertex)` on its own `else`.
### Operating System
macOS 26.5.2
### Salesforce CLI Version
@salesforce/cli/2.147.7 darwin-arm64 node-v24.5.0
### Code Analyzer Plugin (code-analyzer) Version
code-analyzer 5.15.0
### Node Version
v24.5.0
### Java Version
openjdk version "11.0.32" 2026-07-21
### Python Version
N/A
### Additional Context (Screenshots, Files, etc)
`Map` is the standard way to accept a loosely typed payload in an `@AuraEnabled` method, and indexing a list pulled out of one is routine.
Costs three `@AuraEnabled` entry points in our codebase via two different routes — the map shape above, and a static initializer in a shared constants class that indexes a `String.split()` result. The second is the worse pattern: because it sits in a constants class, every entry point that touches it inherits the abort.
### Workaround
Assign through an intermediate typed local that sfge can resolve, or avoid indexing values pulled out of an `Object`-valued map on paths that reach DML. Neither is dependable.
### Urgency
Moderate
Contributor guide
Research direction
Start with PathScopeVisitor.java around lines 804-806 and 874-902, then inspect ApexMapValue.java:183-191 and JSONDeserializeFactory.java:78-79 for other indeterminate values. Reproduce the issue with the provided sf code-analyzer command and Apex class. Done means the entry point completes analysis without InternalExecutionError when an indeterminate value is indexed, while preserving expected findings and coverage.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- security, tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100