forcedotcom / forcedotcom/code-analyzer

[BUG][code-analyzer] sfge: JSON.deserialize with a Type variable throws ClassCastException and aborts the entry point

Aperta Adatta ai principianti
#2,094 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub
Lingua principale
TypeScript
Stelle
240
Fork
52
Merge medio
1g 23h
PR unite (30g)
5

Descrizione

### 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`

`JSONDeserializeFactory` assumes the second argument to `JSON.deserialize` is always a class literal, and casts without checking:

```java
// JSONDeserializeFactory.java:45-52
// This results in a CastExpression that wraps a MethodCallExpression. The
// second parameter is always
// a ClassRefExpressionVertex that denotes the Type
// (MyObject__c)JSON.deserialize(asJson, MyObject__c.class);

ApexValue.validateParameterSize(vertex, 2);
final ClassRefExpressionVertex classRefExpression =
(ClassRefExpressionVertex) vertex.getParameters().get(1);
```

The comment says "always", but `JSON.deserialize(String, System.Type)` accepts any `Type` expression, and dynamic deserialization via `Type.forName(...)` is a normal pattern. When the argument is a variable, the cast fails.

```apex
Type targetType = Type.forName('Account');
Object o = JSON.deserialize(body, targetType);
```

### Output / Logs

```shell
ClassCastException: class com.salesforce.graph.vertex.VariableExpressionVertex$Single cannot be cast to
class com.salesforce.graph.vertex.ClassRefExpressionVertex (...are in unnamed module of loader 'app'):
com.salesforce.graph.symbols.JSONDeserializeFactory.lambda$static$0(JSONDeserializeFactory.java:52);
com.salesforce.graph.ops.ApexStandardLibraryUtil.getStandardType(ApexStandardLibraryUtil.java:155);
com.salesforce.graph.symbols.PathScopeVisitor.afterVisit(PathScopeVisitor.java:1244); ...
```

### 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/JsonDeserializeVarType.cls` with the class shown below, plus a standard `JsonDeserializeVarType.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 JsonDeserializeVarType {
@AuraEnabled
public static void run(String body) {
Type targetType = Type.forName('Account');
Object o = JSON.deserialize(body, targetType);
Account a = (Account) o;
insert a;
}
}
```

### Expected Behavior

When the type argument is not a `ClassRefExpressionVertex`, return an indeterminate value rather than throwing. The type genuinely is not statically known, which is a normal analysis outcome, not an engine error. An `instanceof` guard before the cast is sufficient.

### 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)

Worth prioritising beyond its frequency (1 signature / 1 entry point for us): `JSON.deserialize` into a runtime-chosen type is precisely the shape a taint-tracking engine most wants to reason about, and today it is the shape that reliably kills the analysis instead.

### Workaround

Use a class literal where the type is statically known (`JSON.deserialize(body, Account.class)`). No workaround exists when the target type is genuinely dynamic.

### Urgency

Moderate

Guida per i contributori

Apri la guida per i contributori

Direzione di ricerca

Start in JSONDeserializeFactory.java around lines 45-52 and inspect how the second JSON.deserialize parameter is converted to a ClassRefExpressionVertex. Use the supplied Type.forName reproduction to verify the dynamic argument no longer aborts analysis. Done means the entry point completes without an InternalExecutionError and treats the non-class-literal type as indeterminate.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
java
Ambito
devtools, security
Tipo di issue
Bug
Difficoltà
2/5
Tempo stimato
1-3 ore
Stato di attività
Attiva
Chiarezza
Specificata chiaramente
Idoneità per principianti
78/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.