Skip to content

Commit eefcf96

Browse files
SONARPY-1158 Rename ExpressionTrace to ExpressionFlow (#1225)
1 parent 45e6c67 commit eefcf96

9 files changed

+51
-55
lines changed

python-checks/src/main/java/org/sonar/python/checks/cdk/CdkUtils.java

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@
1919
*/
2020
package org.sonar.python.checks.cdk;
2121

22-
import java.util.ArrayList;
23-
import java.util.Collections;
24-
import java.util.List;
22+
import java.util.Deque;
23+
import java.util.LinkedList;
2524
import java.util.Optional;
2625
import java.util.function.Predicate;
2726
import org.sonar.plugins.python.api.PythonCheck;
@@ -68,12 +67,12 @@ public static Optional<CallExpression> getCall(Expression expression, String fqn
6867
/**
6968
* Resolve a particular argument of a call or get an empty optional if the argument is not set.
7069
*/
71-
protected static Optional<ExpressionTrace> getArgument(SubscriptionContext ctx, CallExpression callExpression, String argumentName) {
70+
protected static Optional<ExpressionFlow> getArgument(SubscriptionContext ctx, CallExpression callExpression, String argumentName) {
7271
return callExpression.arguments().stream()
7372
.map(RegularArgument.class::cast)
7473
.filter(regularArgument -> regularArgument.keywordArgument() != null)
7574
.filter(regularArgument -> argumentName.equals(regularArgument.keywordArgument().name()))
76-
.map(regularArgument -> ExpressionTrace.build(ctx, regularArgument.expression()))
75+
.map(regularArgument -> ExpressionFlow.build(ctx, regularArgument.expression()))
7776
.findAny();
7877
}
7978

@@ -85,43 +84,43 @@ public static Optional<ResolvedKeyValuePair> getKeyValuePair(SubscriptionContext
8584
}
8685

8786
/**
88-
* The expression trace reflects the complete flow of a value across the code.
87+
* The expression flow represents the propagation of an expression.
8988
* It serves as a resolution path from the use of the expression up to the value assignment.
90-
* For example, if the value of an argument expression did not occur directly in the function call, the value can be traced back.
91-
* The trace allows on the one hand to check the assigned value
89+
* For example, if the value of an argument expression did not occur directly in the function call, the value can be tracked back.
90+
* The flow allows on the one hand to check the assigned value
9291
* and on the other hand to display the assignment path of the relevant value when creating an issue.
9392
*/
94-
static class ExpressionTrace {
93+
static class ExpressionFlow {
9594

9695
private static final String TAIL_MESSAGE = "Propagated setting.";
9796

9897
private final SubscriptionContext ctx;
99-
private final List<Expression> trace;
98+
private final Deque<Expression> locations;
10099

101-
private ExpressionTrace(SubscriptionContext ctx, List<Expression> trace) {
100+
private ExpressionFlow(SubscriptionContext ctx, Deque<Expression> locations) {
102101
this.ctx = ctx;
103-
this.trace = Collections.unmodifiableList(trace);
102+
this.locations = locations;
104103
}
105104

106-
protected static ExpressionTrace build(SubscriptionContext ctx, Expression expression) {
107-
List<Expression> trace = new ArrayList<>();
108-
buildTrace(expression, trace);
109-
return new ExpressionTrace(ctx, trace);
105+
protected static ExpressionFlow build(SubscriptionContext ctx, Expression expression) {
106+
Deque<Expression> locations = new LinkedList<>();
107+
resolveLocations(expression, locations);
108+
return new ExpressionFlow(ctx, locations);
110109
}
111110

112-
static void buildTrace(Expression expression, List<Expression> trace) {
113-
trace.add(expression);
111+
static void resolveLocations(Expression expression, Deque<Expression> locations) {
112+
locations.add(expression);
114113
if (expression.is(Tree.Kind.NAME)) {
115114
Expression singleAssignedValue = Expressions.singleAssignedValue(((Name) expression));
116-
if (singleAssignedValue != null && !trace.contains(singleAssignedValue)) {
117-
buildTrace(singleAssignedValue, trace);
115+
if (singleAssignedValue != null && !locations.contains(singleAssignedValue)) {
116+
resolveLocations(singleAssignedValue, locations);
118117
}
119118
}
120119
}
121120

122121
public void addIssue(String primaryMessage) {
123-
PythonCheck.PreciseIssue issue = ctx.addIssue(trace.get(0).parent(), primaryMessage);
124-
trace.stream().skip(1).forEach(expression -> issue.secondary(expression.parent(), TAIL_MESSAGE));
122+
PythonCheck.PreciseIssue issue = ctx.addIssue(locations.getFirst().parent(), primaryMessage);
123+
locations.stream().skip(1).forEach(expression -> issue.secondary(expression.parent(), TAIL_MESSAGE));
125124
}
126125

127126
public void addIssueIf(Predicate<Expression> predicate, String primaryMessage) {
@@ -137,15 +136,15 @@ public void addIssueIf(Predicate<Expression> predicate, String primaryMessage, C
137136
}
138137

139138
public boolean hasExpression(Predicate<Expression> predicate) {
140-
return trace.stream().anyMatch(predicate);
139+
return locations.stream().anyMatch(predicate);
141140
}
142141

143142
public Optional<Expression> getExpression(Predicate<Expression> predicate) {
144-
return trace.stream().filter(predicate).findFirst();
143+
return locations.stream().filter(predicate).findFirst();
145144
}
146145

147-
public List<Expression> trace() {
148-
return trace;
146+
public Deque<Expression> locations() {
147+
return locations;
149148
}
150149
}
151150

@@ -154,16 +153,16 @@ public List<Expression> trace() {
154153
*/
155154
static class ResolvedKeyValuePair {
156155

157-
final ExpressionTrace key;
158-
final ExpressionTrace value;
156+
final ExpressionFlow key;
157+
final ExpressionFlow value;
159158

160-
private ResolvedKeyValuePair(ExpressionTrace key, ExpressionTrace value) {
159+
private ResolvedKeyValuePair(ExpressionFlow key, ExpressionFlow value) {
161160
this.key = key;
162161
this.value = value;
163162
}
164163

165164
static ResolvedKeyValuePair build(SubscriptionContext ctx, KeyValuePair pair) {
166-
return new ResolvedKeyValuePair(ExpressionTrace.build(ctx, pair.key()), ExpressionTrace.build(ctx, pair.value()));
165+
return new ResolvedKeyValuePair(ExpressionFlow.build(ctx, pair.key()), ExpressionFlow.build(ctx, pair.value()));
167166
}
168167
}
169168
}

python-checks/src/main/java/org/sonar/python/checks/cdk/ClearTextProtocolsCheckPart.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,9 @@ public static List<DictionaryLiteral> getDictionaryInList(SubscriptionContext ct
210210
.collect(Collectors.toList());
211211
}
212212

213-
private static List<CdkUtils.ExpressionTrace> getListElements(SubscriptionContext ctx, ListLiteral list) {
213+
private static List<CdkUtils.ExpressionFlow> getListElements(SubscriptionContext ctx, ListLiteral list) {
214214
return list.elements().expressions().stream()
215-
.map(expression -> CdkUtils.ExpressionTrace.build(ctx, expression))
215+
.map(expression -> CdkUtils.ExpressionFlow.build(ctx, expression))
216216
.collect(Collectors.toList());
217217
}
218218

python-checks/src/main/java/org/sonar/python/checks/cdk/DisabledESDomainEncryptionCheck.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ protected void registerFqnConsumer() {
6565
private static BiConsumer<SubscriptionContext, CallExpression> checkDomain(String encryptionArgName, String argFqnMethod, String engine) {
6666
return (ctx, callExpression) -> getArgument(ctx, callExpression, encryptionArgName)
6767
.ifPresentOrElse(
68-
argEncryptedTrace -> argEncryptedTrace.addIssueIf(isSensitiveOptionObj(ctx, argFqnMethod).or(isSensitiveOptionDict(ctx)), unencryptedMessage(engine)),
68+
flow -> flow.addIssueIf(isSensitiveOptionObj(ctx, argFqnMethod).or(isSensitiveOptionDict(ctx)), unencryptedMessage(engine)),
6969
() -> ctx.addIssue(callExpression.callee(), omittingMessage(encryptionArgName, engine))
7070
);
7171
}
@@ -117,6 +117,6 @@ private static Predicate<KeyValuePair> isKey(String keyName) {
117117
}
118118

119119
private static Predicate<KeyValuePair> isValueFalse(SubscriptionContext ctx) {
120-
return keyValuePair -> CdkUtils.ExpressionTrace.build(ctx, keyValuePair.value()).hasExpression(isFalse());
120+
return keyValuePair -> CdkUtils.ExpressionFlow.build(ctx, keyValuePair.value()).hasExpression(isFalse());
121121
}
122122
}

python-checks/src/main/java/org/sonar/python/checks/cdk/DisabledRDSEncryptionCheck.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ protected void registerFqnConsumer() {
5050
}
5151

5252
protected void checkDatabaseArguments(SubscriptionContext ctx, CallExpression resourceConstructor) {
53-
Optional<CdkUtils.ExpressionTrace> argEncrypted = CdkUtils.getArgument(ctx, resourceConstructor, ARG_ENCRYPTED);
54-
Optional<CdkUtils.ExpressionTrace> argEncryptionKey = CdkUtils.getArgument(ctx, resourceConstructor, ARG_ENCRYPTION_KEY);
53+
Optional<CdkUtils.ExpressionFlow> argEncrypted = CdkUtils.getArgument(ctx, resourceConstructor, ARG_ENCRYPTED);
54+
Optional<CdkUtils.ExpressionFlow> argEncryptionKey = CdkUtils.getArgument(ctx, resourceConstructor, ARG_ENCRYPTION_KEY);
5555

5656
if (argEncrypted.isEmpty() && argEncryptionKey.isEmpty()) {
5757
ctx.addIssue(resourceConstructor.callee(), DB_OMITTING_MESSAGE);
@@ -69,14 +69,14 @@ protected void checkDatabaseArguments(SubscriptionContext ctx, CallExpression re
6969

7070
protected void checkCfnDatabaseArguments(SubscriptionContext ctx, CallExpression resourceConstructor) {
7171
CdkUtils.getArgument(ctx, resourceConstructor, ARG_ENCRYPTED).ifPresentOrElse(
72-
argumentTrace -> argumentTrace.addIssueIf(isFalse(), UNENCRYPTED_MESSAGE),
72+
flow -> flow.addIssueIf(isFalse(), UNENCRYPTED_MESSAGE),
7373
() -> ctx.addIssue(resourceConstructor.callee(), CFNDB_OMITTING_MESSAGE)
7474
);
7575
}
7676

7777
protected boolean isEngineAurora(SubscriptionContext ctx, CallExpression resourceConstructor) {
7878
return CdkUtils.getArgument(ctx, resourceConstructor, "engine")
79-
.filter(argumentTrace -> argumentTrace.hasExpression(startsWith("aurora"))).isPresent();
79+
.filter(flow -> flow.hasExpression(startsWith("aurora"))).isPresent();
8080
}
8181
}
8282

python-checks/src/main/java/org/sonar/python/checks/cdk/S3BucketBlockPublicAccessCheck.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public class S3BucketBlockPublicAccessCheck extends AbstractS3BucketCheck {
5151
@Override
5252
BiConsumer<SubscriptionContext, CallExpression> visitBucketConstructor() {
5353
return (ctx, bucket) -> {
54-
Optional<CdkUtils.ExpressionTrace> blockPublicAccess = getArgument(ctx, bucket, "block_public_access");
54+
Optional<CdkUtils.ExpressionFlow> blockPublicAccess = getArgument(ctx, bucket, "block_public_access");
5555
if (blockPublicAccess.isPresent()) {
5656
checkBlockPublicAccess(ctx, blockPublicAccess.get());
5757
} else {
@@ -60,9 +60,9 @@ BiConsumer<SubscriptionContext, CallExpression> visitBucketConstructor() {
6060
};
6161
}
6262

63-
private static void checkBlockPublicAccess(SubscriptionContext ctx, CdkUtils.ExpressionTrace blockPublicAccess) {
63+
private static void checkBlockPublicAccess(SubscriptionContext ctx, CdkUtils.ExpressionFlow blockPublicAccess) {
6464
blockPublicAccess.addIssueIf(S3BucketBlockPublicAccessCheck::blocksAclsOnly, MESSAGE);
65-
blockPublicAccess.trace().stream().filter(CallExpression.class::isInstance).map(CallExpression.class::cast)
65+
blockPublicAccess.locations().stream().filter(CallExpression.class::isInstance).map(CallExpression.class::cast)
6666
.filter(S3BucketBlockPublicAccessCheck::isBlockPublicAccessConstructor)
6767
.findAny()
6868
.ifPresent(bpaConstructor -> visitBlockPublicAccessConstructor(ctx, bpaConstructor));
@@ -74,7 +74,7 @@ private static void visitBlockPublicAccessConstructor(SubscriptionContext ctx, C
7474
.filter(Optional::isPresent)
7575
.map(Optional::get)
7676
.collect(Collectors.toList())
77-
.forEach(argumentTrace -> argumentTrace.addIssueIf(isFalse(), MESSAGE));
77+
.forEach(flow -> flow.addIssueIf(isFalse(), MESSAGE));
7878
}
7979

8080
private static boolean blocksAclsOnly(Expression expression) {

python-checks/src/main/java/org/sonar/python/checks/cdk/S3BucketGrantedAccessCheck.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import org.sonar.plugins.python.api.SubscriptionContext;
2727
import org.sonar.plugins.python.api.symbols.Symbol;
2828
import org.sonar.plugins.python.api.tree.CallExpression;
29-
import org.sonar.plugins.python.api.tree.Expression;
3029
import org.sonar.plugins.python.api.tree.ImportFrom;
3130
import org.sonar.plugins.python.api.tree.Name;
3231
import org.sonar.plugins.python.api.tree.QualifiedExpression;
@@ -89,10 +88,8 @@ BiConsumer<SubscriptionContext, CallExpression> visitBucketConstructor() {
8988
.ifPresent(argument -> argument.addIssueIf(CdkPredicate.isFqnOf(S3_BUCKET_SENSITIVE_POLICIES), getSensitivePolicyMessage(argument)));
9089
}
9190

92-
private static String getSensitivePolicyMessage(CdkUtils.ExpressionTrace argumentTrace){
93-
Expression lastExpression = argumentTrace.trace()
94-
.get(argumentTrace.trace().size()-1);
95-
String attribute = ((QualifiedExpression) lastExpression).name().name();
91+
private static String getSensitivePolicyMessage(CdkUtils.ExpressionFlow flow){
92+
String attribute = ((QualifiedExpression) flow.locations().getLast()).name().name();
9693
return String.format(MESSAGE_POLICY, attribute);
9794
}
9895

python-checks/src/main/java/org/sonar/python/checks/cdk/UnencryptedEbsVolumeCheck.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public class UnencryptedEbsVolumeCheck extends AbstractCdkResourceCheck {
3434
protected void registerFqnConsumer() {
3535
checkFqn("aws_cdk.aws_ec2.Volume", (ctx, volume) ->
3636
getArgument(ctx, volume, "encrypted").ifPresentOrElse(
37-
argumentTrace -> argumentTrace.addIssueIf(isFalse(), PRIMARY_MESSAGE),
37+
flow -> flow.addIssueIf(isFalse(), PRIMARY_MESSAGE),
3838
() -> ctx.addIssue(volume.callee(), OMITTING_MESSAGE)));
3939
}
4040
}

python-checks/src/main/java/org/sonar/python/checks/cdk/UnencryptedSqsQueueCheck.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,17 @@ protected void registerFqnConsumer() {
4343

4444
protected void checkQueue(SubscriptionContext ctx, CallExpression resourceConstructor) {
4545
getArgument(ctx, resourceConstructor, "encryption").ifPresentOrElse(
46-
argumentTrace -> {
47-
argumentTrace.addIssueIf(isFqn("aws_cdk.aws_sqs.QueueEncryption.UNENCRYPTED"), UNENCRYPTED_MESSAGE);
48-
argumentTrace.addIssueIf(isNone(), NONE_MESSAGE);
46+
flow -> {
47+
flow.addIssueIf(isFqn("aws_cdk.aws_sqs.QueueEncryption.UNENCRYPTED"), UNENCRYPTED_MESSAGE);
48+
flow.addIssueIf(isNone(), NONE_MESSAGE);
4949
},
5050
() -> ctx.addIssue(resourceConstructor.callee(), OMITTING_MESSAGE)
5151
);
5252
}
5353

5454
protected void checkCfnQueue(SubscriptionContext ctx, CallExpression resourceConstructor) {
5555
getArgument(ctx, resourceConstructor, "kms_master_key_id").ifPresentOrElse(
56-
argumentTrace -> argumentTrace.addIssueIf(isNone(), CFN_NONE_MESSAGE),
56+
flow -> flow.addIssueIf(isNone(), CFN_NONE_MESSAGE),
5757
() -> ctx.addIssue(resourceConstructor.callee(), CFN_OMITTING_MESSAGE)
5858
);
5959
}

python-checks/src/main/java/org/sonar/python/checks/cdk/WeakSSLProtocolCheckPart.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,20 @@ protected void registerFqnConsumer() {
6363

6464
private static BiConsumer<SubscriptionContext, CallExpression> checkDomainName(Predicate<Expression> predicateIssue) {
6565
return (ctx, callExpression) -> CdkUtils.getArgument(ctx, callExpression, "security_policy").ifPresent(
66-
argTrace -> argTrace.addIssueIf(predicateIssue, ENFORCE_MESSAGE)
66+
flow -> flow.addIssueIf(predicateIssue, ENFORCE_MESSAGE)
6767
);
6868
}
6969

7070
private static BiConsumer<SubscriptionContext, CallExpression> checkDomain(Predicate<Expression> predicateIssue) {
7171
return (ctx, callExpression) -> CdkUtils.getArgument(ctx, callExpression, TLS_SECURITY_POLICY).ifPresentOrElse(
72-
argTrace -> argTrace.addIssueIf(predicateIssue, ENFORCE_MESSAGE),
72+
flow -> flow.addIssueIf(predicateIssue, ENFORCE_MESSAGE),
7373
() -> ctx.addIssue(callExpression.callee(), OMITTING_MESSAGE)
7474
);
7575
}
7676

7777
private static BiConsumer<SubscriptionContext, CallExpression> checkCfnDomain(String domainOptionName) {
7878
return (ctx, callExpression) -> CdkUtils.getArgument(ctx, callExpression, "domain_endpoint_options").ifPresentOrElse(
79-
argTrace -> argTrace.addIssueIf(isSensitiveOptionObj(ctx, domainOptionName)
79+
flow -> flow.addIssueIf(isSensitiveOptionObj(ctx, domainOptionName)
8080
.or(isSensitiveDictionaryTls(ctx)), ENFORCE_MESSAGE),
8181
() -> ctx.addIssue(callExpression.callee(), OMITTING_MESSAGE)
8282
);

0 commit comments

Comments
 (0)