From f1c9ec8a832ace11f063293b9c4c93ff787dcf29 Mon Sep 17 00:00:00 2001 From: san-zrl Date: Fri, 14 Aug 2026 12:33:41 +0200 Subject: [PATCH 01/13] engine: add withOtherParameters() prefix-match mode to detection rule DSL - Add SOME_WITH_REMAINDER to CapturedParameterScope so a rule matches calls whose actual arity is >= the declared arity - Expose withOtherParameters() on every terminal builder stage in IDetectionRule; implement in DetectionRuleBuilderImpl - MethodMatcher: add boolean prefixMatch field and prefixMatchesParameters(); keep 3-arg array constructor as a backward-compatible delegate (prefixMatch=false) - MethodMatcherSerializer: emit prefixMatch boolean field in exported JSON - DetectionRuleStore: append prefixMatch flag to matcher ID to prevent silent deduplication of rules that differ only in arity mode Signed-off-by: san-zrl --- .../ibm/engine/detection/MethodMatcher.java | 27 ++++++++++++++++++- .../com/ibm/engine/rule/IDetectionRule.java | 12 +++++++++ .../builder/DetectionRuleBuilderImpl.java | 27 ++++++++++++++++++- .../engine/serializer/DetectionRuleStore.java | 1 + .../serializer/MethodMatcherSerializer.java | 2 ++ 5 files changed, 67 insertions(+), 2 deletions(-) diff --git a/engine/src/main/java/com/ibm/engine/detection/MethodMatcher.java b/engine/src/main/java/com/ibm/engine/detection/MethodMatcher.java index dc59a5884..f4f2a21e3 100644 --- a/engine/src/main/java/com/ibm/engine/detection/MethodMatcher.java +++ b/engine/src/main/java/com/ibm/engine/detection/MethodMatcher.java @@ -43,6 +43,7 @@ public final class MethodMatcher { @Nonnull private final List invokedObjectTypeStringsSerializable; @Nonnull private final List methodNamesSerializable; @Nonnull private final List parameterTypesSerializable; + private final boolean prefixMatch; public MethodMatcher( @Nonnull String invokedObjectTypeString, @@ -52,6 +53,7 @@ public MethodMatcher( this.invokedObjectTypeStringsSerializable = List.of(invokedObjectTypeString); this.methodNamesSerializable = List.of(methodName); this.parameterTypesSerializable = parameterTypes; + this.prefixMatch = false; this.invokedObjectTypeString = createPredicate(invokedObjectTypeString, (type1 -> (iType -> iType.is(type1)))); @@ -73,10 +75,19 @@ public MethodMatcher( @Nonnull String[] invokedObjectTypeStrings, @Nonnull String[] methodNames, @Nonnull List parameterTypes) { + this(invokedObjectTypeStrings, methodNames, parameterTypes, false); + } + + public MethodMatcher( + @Nonnull String[] invokedObjectTypeStrings, + @Nonnull String[] methodNames, + @Nonnull List parameterTypes, + boolean prefixMatch) { this.invokedObjectTypeStringsSerializable = Arrays.asList(invokedObjectTypeStrings); this.methodNamesSerializable = Arrays.asList(methodNames); this.parameterTypesSerializable = parameterTypes; + this.prefixMatch = prefixMatch; this.invokedObjectTypeString = createPredicate( @@ -95,7 +106,9 @@ public MethodMatcher( type -> type.is(parameterType), parameterType)) .toList(); this.parameterTypes = - (List actualTypes) -> exactMatchesParameters(types, actualTypes); + prefixMatch + ? (List actualTypes) -> prefixMatchesParameters(types, actualTypes) + : (List actualTypes) -> exactMatchesParameters(types, actualTypes); } public MethodMatcher( @@ -104,6 +117,7 @@ public MethodMatcher( this.invokedObjectTypeStringsSerializable = Arrays.asList(invokedObjectTypeStrings); this.methodNamesSerializable = Arrays.asList(methodNames); this.parameterTypesSerializable = List.of(); + this.prefixMatch = false; this.invokedObjectTypeString = createPredicate( @@ -146,6 +160,13 @@ private boolean exactMatchesParameters( && matchesParameters(expectedTypes, actualTypes); } + private boolean prefixMatchesParameters( + @Nonnull List> expectedTypes, @Nonnull List actualTypes) { + return !expectedTypes.isEmpty() + && actualTypes.size() >= expectedTypes.size() + && matchesParameters(expectedTypes, actualTypes); + } + private boolean matchesParameters( @Nonnull List> expectedTypes, @Nonnull List actualTypes) { for (int i = 0; i < expectedTypes.size(); i++) { @@ -244,4 +265,8 @@ public List getMethodNamesSerializable() { public List getParameterTypesSerializable() { return this.parameterTypesSerializable; } + + public boolean isPrefixMatch() { + return this.prefixMatch; + } } diff --git a/engine/src/main/java/com/ibm/engine/rule/IDetectionRule.java b/engine/src/main/java/com/ibm/engine/rule/IDetectionRule.java index 17c4d7662..bbc9330e1 100644 --- a/engine/src/main/java/com/ibm/engine/rule/IDetectionRule.java +++ b/engine/src/main/java/com/ibm/engine/rule/IDetectionRule.java @@ -102,6 +102,9 @@ interface ParametersFactoryBuilder { ParametersFinalDetectionRuleBuilder addDependingDetectionRules( @Nonnull List> detectionRules); + @Nonnull + FinalDetectionRuleBuilder withOtherParameters(); + @Nonnull AddBundleDetectionRuleBuilder buildForContext( @Nonnull IDetectionContext detectionValueContext); @@ -121,6 +124,9 @@ interface PositionBuilder { ParametersFinalDetectionRuleBuilder addDependingDetectionRules( @Nonnull List> detectionRules); + @Nonnull + FinalDetectionRuleBuilder withOtherParameters(); + @Nonnull AddBundleDetectionRuleBuilder buildForContext( @Nonnull IDetectionContext detectionValueContext); @@ -137,6 +143,9 @@ interface ParametersDependingRulesBuilder { ParametersFinalDetectionRuleBuilder addDependingDetectionRules( @Nonnull List> detectionRules); + @Nonnull + FinalDetectionRuleBuilder withOtherParameters(); + @Nonnull AddBundleDetectionRuleBuilder buildForContext( @Nonnull IDetectionContext detectionValueContext); @@ -149,6 +158,9 @@ interface ParametersFinalDetectionRuleBuilder { @Nonnull ParametersFactoryBuilder withMethodParameterMatchExactType(@Nonnull String type); + @Nonnull + FinalDetectionRuleBuilder withOtherParameters(); + @Nonnull AddBundleDetectionRuleBuilder buildForContext( @Nonnull IDetectionContext detectionValueContext); diff --git a/engine/src/main/java/com/ibm/engine/rule/builder/DetectionRuleBuilderImpl.java b/engine/src/main/java/com/ibm/engine/rule/builder/DetectionRuleBuilderImpl.java index 75f6b47f0..e1ab7be5b 100644 --- a/engine/src/main/java/com/ibm/engine/rule/builder/DetectionRuleBuilderImpl.java +++ b/engine/src/main/java/com/ibm/engine/rule/builder/DetectionRuleBuilderImpl.java @@ -314,6 +314,29 @@ public IDetectionRule.FinalDetectionRuleBuilder withAnyParameters() { bundle); } + @Nonnull + @Override + public IDetectionRule.FinalDetectionRuleBuilder withOtherParameters() { + checkDetectionParameterState(); + this.capturedParameterScope = CapturedParameterScope.SOME_WITH_REMAINDER; + return new DetectionRuleBuilderImpl<>( + objectTypes, + methodNames, + parameters, + capturedParameterScope, + detectionValueContext, + shouldMatchExactTypes, + invokedObjectDependingDetectionRules, + parameterType, + iValueFactory, + iActionFactory, + detectionRules, + positionMove, + parameterShouldMatchExactTypes, + buildingNewDetectionParameter, + bundle); + } + @Nonnull @Override public IDetectionRule.ParametersDependingRulesBuilder asChildOfParameterWithId(int id) { @@ -489,7 +512,8 @@ private IDetectionRule build() { new MethodMatcher<>( this.objectTypes, this.methodNames, - this.parameters.stream().map(Parameter::getParameterType).toList()); + this.parameters.stream().map(Parameter::getParameterType).toList(), + capturedParameterScope == CapturedParameterScope.SOME_WITH_REMAINDER); return new DetectionRule<>( methodMatcher, @@ -543,6 +567,7 @@ private void checkDetectionParameterState() { enum CapturedParameterScope { SOME, + SOME_WITH_REMAINDER, ANY, NONE } diff --git a/engine/src/main/java/com/ibm/engine/serializer/DetectionRuleStore.java b/engine/src/main/java/com/ibm/engine/serializer/DetectionRuleStore.java index 0dd10d23d..e509a7576 100644 --- a/engine/src/main/java/com/ibm/engine/serializer/DetectionRuleStore.java +++ b/engine/src/main/java/com/ibm/engine/serializer/DetectionRuleStore.java @@ -72,6 +72,7 @@ public static String getMatcherID(MethodMatcher methodMatcher) { for (String parameterType : methodMatcher.getParameterTypesSerializable()) { stringID += parameterType + " "; } + stringID += "| " + methodMatcher.isPrefixMatch(); return stringID; } diff --git a/engine/src/main/java/com/ibm/engine/serializer/MethodMatcherSerializer.java b/engine/src/main/java/com/ibm/engine/serializer/MethodMatcherSerializer.java index 1261c7a2f..df5a5988f 100644 --- a/engine/src/main/java/com/ibm/engine/serializer/MethodMatcherSerializer.java +++ b/engine/src/main/java/com/ibm/engine/serializer/MethodMatcherSerializer.java @@ -63,6 +63,8 @@ public void serialize(MethodMatcher matcher, JsonGenerator jgen, SerializerProvi } jgen.writeEndArray(); + jgen.writeBooleanField("prefixMatch", matcher.isPrefixMatch()); + jgen.writeEndObject(); } } From 1bac9a8ece7a7574903e72dafa8df918d3e05198 Mon Sep 17 00:00:00 2001 From: san-zrl Date: Fri, 14 Aug 2026 12:34:03 +0200 Subject: [PATCH 02/13] docs: document withOtherParameters() in the detection-rule grammar Signed-off-by: san-zrl --- docs/DETECTION_RULE_STRUCTURE.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/DETECTION_RULE_STRUCTURE.md b/docs/DETECTION_RULE_STRUCTURE.md index 924c61e81..2c65b4eb4 100644 --- a/docs/DETECTION_RULE_STRUCTURE.md +++ b/docs/DETECTION_RULE_STRUCTURE.md @@ -40,6 +40,9 @@ new DetectionRuleBuilder() ]? [.addDependingDetectionRules(detectionRules)]? ]+ + [ + .withOtherParameters() + ] .buildForContext(detectionValueContext) .inBundle(bundle) .withDependingDetectionRules(detectionRules) | .withoutDependingDetectionRules() @@ -94,6 +97,8 @@ In the tree of detected values, the values detected by these dependent detection At this point, you should have repeated all the steps starting from the `withMethodParameter` to here as many times as there are parameters in the function that you want to capture. +The `withMethodParameter` section may be followed by one `withOtherParameters` indicating that the rule still matches even if an arbirary number of additional parameters follows. This feature should be used with care since it may lead to overlapping rules that cause duplicate detections. In languges like Python it allows the matching of combinations of optional parameters without analysing them in detail. + Then, `buildForContext(IDetectionContext detectionValueContext)` defines the detection context ([`IDetectionContext`](../engine/src/main/java/com/ibm/engine/model/context/IDetectionContext.java)) for all the detected values of your rule (but detections from dependent rules have their own context). A detection context is therefore linked to each detected value, and is designed to categorize your findings and to help you carry additional information that is not present in the detected value. For example, suppose you have two function calls `Cipher.getInstance("AES")` and `SecretKeyFactory.getInstance("AES")`. When writing detection rules to capture their cryptography information, you will in both cases capture the algorithm value "AES". From 1d460dd8bf349b43fb7d9e0680135cb0433bf219 Mon Sep 17 00:00:00 2001 From: san-zrl Date: Fri, 14 Aug 2026 12:34:31 +0200 Subject: [PATCH 03/13] mapper: fix copy-constructor aliasing in Algorithm/Key and CME in SignatureReorganizer Algorithm(Algorithm,Class) and Key(Key,Class) assigned the source node's live children map by reference; mutating the re-kinded copy corrupted the original node's children. Use new HashMap<>(src.getChildren()) instead. SignatureReorganizer.moveNodesFromUnder*: snapshot node.getChildren() into an ArrayList before iterating and remove each child individually via removeChildOfType() to avoid ConcurrentModificationException on the live map. Signed-off-by: san-zrl --- .../java/com/ibm/mapper/model/Algorithm.java | 2 +- .../main/java/com/ibm/mapper/model/Key.java | 2 +- .../rules/SignatureReorganizer.java | 39 ++++++++++++------- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/mapper/src/main/java/com/ibm/mapper/model/Algorithm.java b/mapper/src/main/java/com/ibm/mapper/model/Algorithm.java index 95c7308c7..575cd8608 100644 --- a/mapper/src/main/java/com/ibm/mapper/model/Algorithm.java +++ b/mapper/src/main/java/com/ibm/mapper/model/Algorithm.java @@ -36,7 +36,7 @@ public class Algorithm implements IAlgorithm { public Algorithm( @Nonnull IAlgorithm algorithm, @Nonnull final Class asKind) { this.name = algorithm.getName(); - this.children = algorithm.getChildren(); + this.children = new HashMap<>(algorithm.getChildren()); this.detectionLocation = algorithm.getDetectionContext(); this.kind = asKind; this.origin = algorithm.getOrigin(); diff --git a/mapper/src/main/java/com/ibm/mapper/model/Key.java b/mapper/src/main/java/com/ibm/mapper/model/Key.java index cf0d98705..ca82e11c9 100644 --- a/mapper/src/main/java/com/ibm/mapper/model/Key.java +++ b/mapper/src/main/java/com/ibm/mapper/model/Key.java @@ -45,7 +45,7 @@ protected Key( @Nonnull DetectionLocation detectionLocation, @Nonnull final Class asKind) { this.name = key.name; - this.children = key.getChildren(); + this.children = new HashMap<>(key.getChildren()); this.detectionLocation = detectionLocation; this.kind = asKind; } diff --git a/mapper/src/main/java/com/ibm/mapper/reorganizer/rules/SignatureReorganizer.java b/mapper/src/main/java/com/ibm/mapper/reorganizer/rules/SignatureReorganizer.java index 5a4708993..73095321f 100644 --- a/mapper/src/main/java/com/ibm/mapper/reorganizer/rules/SignatureReorganizer.java +++ b/mapper/src/main/java/com/ibm/mapper/reorganizer/rules/SignatureReorganizer.java @@ -35,7 +35,6 @@ import com.ibm.mapper.reorganizer.builder.ReorganizerRuleBuilder; import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.Optional; import javax.annotation.Nonnull; @@ -194,12 +193,19 @@ public static IReorganizerRule moveNodesFromUnderFunctionalityUnderNode( .flatMap(p -> p.hasChildOfType(underNodeClazz)) .ifPresent( n -> { - for (Map.Entry, INode> - childKeyValue : - node.getChildren().entrySet()) { - n.put(childKeyValue.getValue()); - node.removeChildOfType(childKeyValue.getKey()); - } + // Snapshot the children before mutating so that + // we neither trigger + // ConcurrentModificationException + // nor corrupt a shared map aliased by Algorithm's + // copy constructor (Algorithm.java:39). + new java.util.ArrayList<>( + node.getChildren().values()) + .forEach( + child -> { + n.put(child); + node.removeChildOfType( + child.getKind()); + }); }); return null; }); @@ -252,12 +258,19 @@ public static IReorganizerRule moveNodesFromUnderFunctionalityUnderParent( Optional.ofNullable(parent) .ifPresent( p -> { - for (Map.Entry, INode> - childKeyValue : - node.getChildren().entrySet()) { - p.put(childKeyValue.getValue()); - node.removeChildOfType(childKeyValue.getKey()); - } + // Snapshot the children before mutating so that + // we neither trigger + // ConcurrentModificationException + // nor corrupt a shared map aliased by Algorithm's + // copy constructor (Algorithm.java:39). + new java.util.ArrayList<>( + node.getChildren().values()) + .forEach( + child -> { + p.put(child); + node.removeChildOfType( + child.getKind()); + }); }); return null; }); From e0bfbd38cd2f1440227a0188122d7442730b8252 Mon Sep 17 00:00:00 2001 From: san-zrl Date: Fri, 14 Aug 2026 12:35:20 +0200 Subject: [PATCH 04/13] mapper: add PycaCurveMapper and PycaKeyBasedAlgorithmMapper; extend pyca mappers PycaCurveMapper: centralises curve-string to model mapping. Correctly splits Edwards curves (ED25519->Edwards25519, ED448->Edwards448) from Montgomery curves (CURVE25519->Curve25519, CURVE448->Curve448). Fixes SECP521R1 aliases (was SECP512R1) and re-adds the missing SECP256K1 case. PycaKeyBasedAlgorithmMapper: extracts the RSA/DSA/DH/ElGamal/Fernet algorithm switch that was duplicated across the three key-context translators. PycaCipherMapper: adds DES, RC2, Salsa20, AES128_GCM/AES256_GCM, RC4 alias, CHACHA20_POLY1305 alias. PycaDigestMapper/PycaMacMapper: adds MD2, MD4, RIPEMD-160, KMAC, TupleHash, cSHAKE, Keccak, KangarooTwelve for PyCryptodome rules. RIPEMD: adds (asKind, RIPEMD) copy constructor for MAC usage. Signed-off-by: san-zrl --- .../mapper/mapper/pyca/PycaCipherMapper.java | 13 +- .../mapper/mapper/pyca/PycaCurveMapper.java | 113 ++++++++++++++++++ .../mapper/mapper/pyca/PycaDigestMapper.java | 18 ++- .../pyca/PycaKeyBasedAlgorithmMapper.java | 64 ++++++++++ .../ibm/mapper/mapper/pyca/PycaMacMapper.java | 13 ++ .../ibm/mapper/model/algorithms/RIPEMD.java | 5 + 6 files changed, 221 insertions(+), 5 deletions(-) create mode 100644 mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaCurveMapper.java create mode 100644 mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaKeyBasedAlgorithmMapper.java diff --git a/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaCipherMapper.java b/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaCipherMapper.java index 5461d068d..6098f1b16 100644 --- a/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaCipherMapper.java +++ b/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaCipherMapper.java @@ -26,12 +26,15 @@ import com.ibm.mapper.model.algorithms.Camellia; import com.ibm.mapper.model.algorithms.ChaCha20; import com.ibm.mapper.model.algorithms.ChaCha20Poly1305; +import com.ibm.mapper.model.algorithms.DES; import com.ibm.mapper.model.algorithms.Fernet; import com.ibm.mapper.model.algorithms.IDEA; +import com.ibm.mapper.model.algorithms.RC2; import com.ibm.mapper.model.algorithms.RC4; import com.ibm.mapper.model.algorithms.RSA; import com.ibm.mapper.model.algorithms.SEED; import com.ibm.mapper.model.algorithms.SM4; +import com.ibm.mapper.model.algorithms.Salsa20; import com.ibm.mapper.model.algorithms.TripleDES; import com.ibm.mapper.model.algorithms.cast.CAST128; import com.ibm.mapper.utils.DetectionLocation; @@ -53,17 +56,21 @@ public final class PycaCipherMapper implements IMapper { case "AES128" -> Optional.of(new AES(128, detectionLocation)); case "AES256" -> Optional.of(new AES(256, detectionLocation)); case "CAMELLIA" -> Optional.of(new Camellia(detectionLocation)); - case "TRIPLEDES" -> Optional.of(new TripleDES(detectionLocation)); + case "TRIPLEDES", "3DES" -> Optional.of(new TripleDES(detectionLocation)); + case "DES" -> Optional.of(new DES(detectionLocation)); case "CAST5" -> Optional.of(new CAST128(detectionLocation)); case "SEED" -> Optional.of(new SEED(detectionLocation)); case "SM4" -> Optional.of(new SM4(detectionLocation)); case "BLOWFISH" -> Optional.of(new Blowfish(detectionLocation)); case "IDEA" -> Optional.of(new IDEA(detectionLocation)); case "CHACHA20" -> Optional.of(new ChaCha20(detectionLocation)); - case "ARC4" -> Optional.of(new RC4(detectionLocation)); + case "SALSA20" -> Optional.of(new Salsa20(detectionLocation)); + case "ARC4", "RC4" -> Optional.of(new RC4(detectionLocation)); + case "RC2" -> Optional.of(new RC2(detectionLocation)); case "FERNET" -> Optional.of(new Fernet(detectionLocation)); case "RSA" -> Optional.of(new RSA(detectionLocation)); - case "CHACHA20POLY1305" -> Optional.of(new ChaCha20Poly1305(detectionLocation)); + case "CHACHA20POLY1305", "CHACHA20_POLY1305" -> + Optional.of(new ChaCha20Poly1305(detectionLocation)); default -> Optional.empty(); }; } diff --git a/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaCurveMapper.java b/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaCurveMapper.java new file mode 100644 index 000000000..b4cee3881 --- /dev/null +++ b/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaCurveMapper.java @@ -0,0 +1,113 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.mapper.mapper.pyca; + +import com.ibm.mapper.mapper.IMapper; +import com.ibm.mapper.model.EllipticCurveAlgorithm; +import com.ibm.mapper.model.curves.Brainpoolp256r1; +import com.ibm.mapper.model.curves.Brainpoolp384r1; +import com.ibm.mapper.model.curves.Brainpoolp512r1; +import com.ibm.mapper.model.curves.Curve25519; +import com.ibm.mapper.model.curves.Curve448; +import com.ibm.mapper.model.curves.Edwards25519; +import com.ibm.mapper.model.curves.Edwards448; +import com.ibm.mapper.model.curves.Secp192r1; +import com.ibm.mapper.model.curves.Secp224r1; +import com.ibm.mapper.model.curves.Secp256k1; +import com.ibm.mapper.model.curves.Secp256r1; +import com.ibm.mapper.model.curves.Secp384r1; +import com.ibm.mapper.model.curves.Secp521r1; +import com.ibm.mapper.model.curves.Sect163k1; +import com.ibm.mapper.model.curves.Sect163r2; +import com.ibm.mapper.model.curves.Sect233k1; +import com.ibm.mapper.model.curves.Sect233r1; +import com.ibm.mapper.model.curves.Sect283k1; +import com.ibm.mapper.model.curves.Sect283r1; +import com.ibm.mapper.model.curves.Sect409k1; +import com.ibm.mapper.model.curves.Sect409r1; +import com.ibm.mapper.model.curves.Sect571k1; +import com.ibm.mapper.model.curves.Sect571r1; +import com.ibm.mapper.utils.DetectionLocation; +import java.util.Optional; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public final class PycaCurveMapper implements IMapper { + + @Nonnull + @Override + public Optional parse( + @Nullable String str, @Nonnull DetectionLocation detectionLocation) { + if (str == null) { + return Optional.empty(); + } + + @Nonnull String curve = str; + return switch (curve.toUpperCase().trim()) { + case "SECP192R1", "PRIME192V1", "P-192", "P192", "NIST P-192" -> + Optional.of(new EllipticCurveAlgorithm(new Secp192r1(detectionLocation))); + case "SECP224R1", "PRIME224V1", "P-224", "P224", "NIST P-224" -> + Optional.of(new EllipticCurveAlgorithm(new Secp224r1(detectionLocation))); + case "SECP256R1", "PRIME256V1", "P-256", "P256", "NIST P-256" -> + Optional.of(new EllipticCurveAlgorithm(new Secp256r1(detectionLocation))); + case "SECP384R1", "PRIME384V1", "P-384", "P384", "NIST P-384" -> + Optional.of(new EllipticCurveAlgorithm(new Secp384r1(detectionLocation))); + case "SECP521R1", "PRIME521V1", "P-521", "P521", "NIST P-521" -> + Optional.of(new EllipticCurveAlgorithm(new Secp521r1(detectionLocation))); + case "SECP256K1" -> + Optional.of(new EllipticCurveAlgorithm(new Secp256k1(detectionLocation))); + case "CURVE25519" -> + Optional.of(new EllipticCurveAlgorithm(new Curve25519(detectionLocation))); + case "ED25519" -> + Optional.of(new EllipticCurveAlgorithm(new Edwards25519(detectionLocation))); + case "CURVE448" -> + Optional.of(new EllipticCurveAlgorithm(new Curve448(detectionLocation))); + case "ED448" -> + Optional.of(new EllipticCurveAlgorithm(new Edwards448(detectionLocation))); + case "BRAINPOOLP256R1" -> + Optional.of(new EllipticCurveAlgorithm(new Brainpoolp256r1(detectionLocation))); + case "BRAINPOOLP384R1" -> + Optional.of(new EllipticCurveAlgorithm(new Brainpoolp384r1(detectionLocation))); + case "BRAINPOOLP512R1" -> + Optional.of(new EllipticCurveAlgorithm(new Brainpoolp512r1(detectionLocation))); + case "SECT571K1" -> + Optional.of(new EllipticCurveAlgorithm(new Sect571k1(detectionLocation))); + case "SECT409K1" -> + Optional.of(new EllipticCurveAlgorithm(new Sect409k1(detectionLocation))); + case "SECT283K1" -> + Optional.of(new EllipticCurveAlgorithm(new Sect283k1(detectionLocation))); + case "SECT233K1" -> + Optional.of(new EllipticCurveAlgorithm(new Sect233k1(detectionLocation))); + case "SECT163K1" -> + Optional.of(new EllipticCurveAlgorithm(new Sect163k1(detectionLocation))); + case "SECT571R1" -> + Optional.of(new EllipticCurveAlgorithm(new Sect571r1(detectionLocation))); + case "SECT409R1" -> + Optional.of(new EllipticCurveAlgorithm(new Sect409r1(detectionLocation))); + case "SECT283R1" -> + Optional.of(new EllipticCurveAlgorithm(new Sect283r1(detectionLocation))); + case "SECT233R1" -> + Optional.of(new EllipticCurveAlgorithm(new Sect233r1(detectionLocation))); + case "SECT163R2" -> + Optional.of(new EllipticCurveAlgorithm(new Sect163r2(detectionLocation))); + default -> Optional.empty(); + }; + } +} diff --git a/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaDigestMapper.java b/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaDigestMapper.java index 8e2a1ca3e..5ab5b40e2 100644 --- a/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaDigestMapper.java +++ b/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaDigestMapper.java @@ -21,14 +21,20 @@ import com.ibm.mapper.mapper.IMapper; import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.algorithms.KangarooTwelve; +import com.ibm.mapper.model.algorithms.Keccak; +import com.ibm.mapper.model.algorithms.MD2; +import com.ibm.mapper.model.algorithms.MD4; import com.ibm.mapper.model.algorithms.MD5; -import com.ibm.mapper.model.algorithms.Poly1305; +import com.ibm.mapper.model.algorithms.RIPEMD; import com.ibm.mapper.model.algorithms.SHA; import com.ibm.mapper.model.algorithms.SHA2; import com.ibm.mapper.model.algorithms.SHA3; import com.ibm.mapper.model.algorithms.SM3; +import com.ibm.mapper.model.algorithms.TupleHash; import com.ibm.mapper.model.algorithms.blake.BLAKE2b; import com.ibm.mapper.model.algorithms.blake.BLAKE2s; +import com.ibm.mapper.model.algorithms.shake.CSHAKE; import com.ibm.mapper.model.algorithms.shake.SHAKE; import com.ibm.mapper.utils.DetectionLocation; import java.util.Optional; @@ -59,11 +65,19 @@ public final class PycaDigestMapper implements IMapper { case "SHA3_512" -> Optional.of(new SHA3(512, detectionLocation)); case "SHAKE128" -> Optional.of(new SHAKE(128, detectionLocation)); case "SHAKE256" -> Optional.of(new SHAKE(256, detectionLocation)); + case "MD2" -> Optional.of(new MD2(detectionLocation)); + case "MD4" -> Optional.of(new MD4(detectionLocation)); case "MD5" -> Optional.of(new MD5(detectionLocation)); case "BLAKE2B" -> Optional.of(new BLAKE2b(false, detectionLocation)); case "BLAKE2S" -> Optional.of(new BLAKE2s(false, detectionLocation)); case "SM3" -> Optional.of(new SM3(detectionLocation)); - case "POLY1305" -> Optional.of(new Poly1305(detectionLocation)); + case "RIPEMD160" -> Optional.of(new RIPEMD(160, detectionLocation)); + case "TUPLEHASH128" -> Optional.of(new TupleHash(128, detectionLocation)); + case "TUPLEHASH256" -> Optional.of(new TupleHash(256, detectionLocation)); + case "KECCAK" -> Optional.of(new Keccak(detectionLocation)); + case "CSHAKE128" -> Optional.of(new CSHAKE(128, detectionLocation)); + case "CSHAKE256" -> Optional.of(new CSHAKE(256, detectionLocation)); + case "KANGAROOTWELVE" -> Optional.of(new KangarooTwelve(detectionLocation)); default -> Optional.empty(); }; } diff --git a/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaKeyBasedAlgorithmMapper.java b/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaKeyBasedAlgorithmMapper.java new file mode 100644 index 000000000..315ae9286 --- /dev/null +++ b/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaKeyBasedAlgorithmMapper.java @@ -0,0 +1,64 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.mapper.mapper.pyca; + +import com.ibm.mapper.mapper.IMapper; +import com.ibm.mapper.model.Algorithm; +import com.ibm.mapper.model.EllipticCurveAlgorithm; +import com.ibm.mapper.model.algorithms.DH; +import com.ibm.mapper.model.algorithms.DSA; +import com.ibm.mapper.model.algorithms.Ed25519; +import com.ibm.mapper.model.algorithms.Ed448; +import com.ibm.mapper.model.algorithms.ElGamal; +import com.ibm.mapper.model.algorithms.Fernet; +import com.ibm.mapper.model.algorithms.RSA; +import com.ibm.mapper.model.curves.Curve25519; +import com.ibm.mapper.model.curves.Curve448; +import com.ibm.mapper.utils.DetectionLocation; +import java.util.Optional; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public final class PycaKeyBasedAlgorithmMapper implements IMapper { + + @Override + public @Nonnull Optional parse( + @Nullable String str, @Nonnull DetectionLocation detectionLocation) { + if (str == null) { + return Optional.empty(); + } + + return switch (str.toUpperCase().trim()) { + case "RSA" -> Optional.of(new RSA(detectionLocation)); + case "DSA" -> Optional.of(new DSA(detectionLocation)); + case "DH" -> Optional.of(new DH(detectionLocation)); + case "EC" -> Optional.of(new EllipticCurveAlgorithm(detectionLocation)); + case "CURVE25519" -> + Optional.of(new EllipticCurveAlgorithm(new Curve25519(detectionLocation))); + case "CURVE448" -> + Optional.of(new EllipticCurveAlgorithm(new Curve448(detectionLocation))); + case "ED25519" -> Optional.of(new Ed25519(detectionLocation)); + case "ED448" -> Optional.of(new Ed448(detectionLocation)); + case "ELGAMAL" -> Optional.of(new ElGamal(detectionLocation)); + case "FERNET" -> Optional.of(new Fernet(detectionLocation)); + default -> Optional.empty(); + }; + } +} diff --git a/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaMacMapper.java b/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaMacMapper.java index 777eca70b..962774180 100644 --- a/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaMacMapper.java +++ b/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaMacMapper.java @@ -28,9 +28,12 @@ import com.ibm.mapper.model.algorithms.ChaCha20; import com.ibm.mapper.model.algorithms.Fernet; import com.ibm.mapper.model.algorithms.IDEA; +import com.ibm.mapper.model.algorithms.KMAC; +import com.ibm.mapper.model.algorithms.MD2; import com.ibm.mapper.model.algorithms.MD5; import com.ibm.mapper.model.algorithms.Poly1305; import com.ibm.mapper.model.algorithms.RC4; +import com.ibm.mapper.model.algorithms.RIPEMD; import com.ibm.mapper.model.algorithms.RSA; import com.ibm.mapper.model.algorithms.SEED; import com.ibm.mapper.model.algorithms.SHA; @@ -42,6 +45,7 @@ import com.ibm.mapper.model.algorithms.blake.BLAKE2b; import com.ibm.mapper.model.algorithms.blake.BLAKE2s; import com.ibm.mapper.model.algorithms.cast.CAST128; +import com.ibm.mapper.model.algorithms.shake.CSHAKE; import com.ibm.mapper.model.algorithms.shake.SHAKE; import com.ibm.mapper.utils.DetectionLocation; import java.util.Optional; @@ -104,13 +108,22 @@ public class PycaMacMapper implements IMapper { case "SHAKE128" -> Optional.of(new SHAKE(Mac.class, new SHAKE(128, detectionLocation))); case "SHAKE256" -> Optional.of(new SHAKE(Mac.class, new SHAKE(256, detectionLocation))); case "MD5" -> Optional.of(new MD5(Mac.class, detectionLocation)); + case "MD2" -> Optional.of(new MD2(Mac.class, detectionLocation)); case "BLAKE2B" -> Optional.of(new BLAKE2b(Mac.class, new BLAKE2b(false, detectionLocation))); case "BLAKE2S" -> Optional.of(new BLAKE2s(Mac.class, new BLAKE2s(false, detectionLocation))); case "SM3" -> Optional.of(new SM3(Mac.class, new SM3(detectionLocation))); + case "KMAC128" -> Optional.of(new KMAC(Mac.class, new KMAC(128, detectionLocation))); + case "KMAC256" -> Optional.of(new KMAC(Mac.class, new KMAC(256, detectionLocation))); case "POLY1305" -> Optional.of(new Poly1305(Mac.class, new Poly1305(detectionLocation))); + case "RIPEMD160" -> + Optional.of(new RIPEMD(Mac.class, new RIPEMD(160, detectionLocation))); + case "CSHAKE128" -> + Optional.of(new CSHAKE(Mac.class, new CSHAKE(128, detectionLocation))); + case "CSHAKE256" -> + Optional.of(new CSHAKE(Mac.class, new CSHAKE(256, detectionLocation))); default -> Optional.empty(); }; } diff --git a/mapper/src/main/java/com/ibm/mapper/model/algorithms/RIPEMD.java b/mapper/src/main/java/com/ibm/mapper/model/algorithms/RIPEMD.java index 4ed5f0860..15766ae7d 100644 --- a/mapper/src/main/java/com/ibm/mapper/model/algorithms/RIPEMD.java +++ b/mapper/src/main/java/com/ibm/mapper/model/algorithms/RIPEMD.java @@ -22,6 +22,7 @@ import com.ibm.mapper.model.Algorithm; import com.ibm.mapper.model.DigestSize; import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.IPrimitive; import com.ibm.mapper.model.MessageDigest; import com.ibm.mapper.utils.DetectionLocation; import java.util.Optional; @@ -72,4 +73,8 @@ public RIPEMD(int digestSize, @Nonnull DetectionLocation detectionLocation) { this(detectionLocation); this.put(new DigestSize(digestSize, detectionLocation)); } + + public RIPEMD(@Nonnull final Class asKind, @Nonnull RIPEMD ripemd) { + super(ripemd, asKind); + } } From 15722c075f4b9b374ddb804a34c0ba816037aa1b Mon Sep 17 00:00:00 2001 From: san-zrl Date: Fri, 14 Aug 2026 12:35:44 +0200 Subject: [PATCH 05/13] python/pyca: move pyca detection rules into detection/pyca/ sub-package Relocate all existing pyca rule classes (aead, asymmetric, fernet, hash, kdf, keyagreement, mac, padding, symmetric, wrapping) into the new detection/pyca/ sub-package to match the pycrypto/ layout and avoid future naming collisions. No logic changes. Signed-off-by: san-zrl --- .../rules/detection/pyca/aead/PycaAEAD.java | 95 +++++++ .../rules/detection/pyca/aead/PycaAES.java | 109 +++++++++ .../detection/pyca/asymmetric/PycaDSA.java | 114 +++++++++ .../pyca/asymmetric/PycaDiffieHellman.java | 111 +++++++++ .../pyca/asymmetric/PycaEllipticCurve.java | 159 ++++++++++++ .../detection/pyca/asymmetric/PycaRSA.java | 204 ++++++++++++++++ .../detection/pyca/asymmetric/PycaSign.java | 77 ++++++ .../detection/pyca/fernet/PycaFernet.java | 95 +++++++ .../rules/detection/pyca/hash/PycaHash.java | 131 ++++++++++ .../rules/detection/pyca/kdf/PycaKDF.java | 231 ++++++++++++++++++ .../pyca/keyagreement/PycaKeyAgreement.java | 76 ++++++ .../rules/detection/pyca/mac/PycaMAC.java | 91 +++++++ .../detection/pyca/padding/PycaPadding.java | 103 ++++++++ .../detection/pyca/symmetric/PycaCipher.java | 133 ++++++++++ .../detection/pyca/wrapping/PycaWrapping.java | 75 ++++++ 15 files changed, 1804 insertions(+) create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pyca/aead/PycaAEAD.java create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pyca/aead/PycaAES.java create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaDSA.java create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaDiffieHellman.java create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaEllipticCurve.java create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaRSA.java create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaSign.java create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaFernet.java create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pyca/hash/PycaHash.java create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaKDF.java create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pyca/keyagreement/PycaKeyAgreement.java create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pyca/mac/PycaMAC.java create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pyca/padding/PycaPadding.java create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher.java create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pyca/wrapping/PycaWrapping.java diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pyca/aead/PycaAEAD.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/aead/PycaAEAD.java new file mode 100644 index 000000000..5c15bc309 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/aead/PycaAEAD.java @@ -0,0 +1,95 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.aead; + +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.context.SecretKeyContext; +import com.ibm.engine.model.factory.CipherActionFactory; +import com.ibm.engine.model.factory.KeyActionFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PycaAEAD { + + private PycaAEAD() { + // private + } + + private static final String TYPE = + "cryptography.hazmat.primitives.ciphers.aead.ChaCha20Poly1305"; + + private static final IDetectionRule ENCRYPT_CHACHA20POLY1305 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(TYPE) + .forMethods("encrypt") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) + .withAnyParameters() + .buildForContext(new CipherContext(Map.of("algorithm", "ChaCha20Poly1305"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule DECRYPT_CHACHA20POLY1305 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(TYPE) + .forMethods("decrypt") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) + .withAnyParameters() + .buildForContext(new CipherContext(Map.of("algorithm", "ChaCha20Poly1305"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule GENERATION_CHACHA20POLY1305 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(TYPE) + .forMethods("generate_key") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withAnyParameters() + .buildForContext( + new SecretKeyContext( + Map.of("algorithm", "ChaCha20Poly1305", "kind", "AEAD"))) + .inBundle(() -> "Pyca") + .withDependingDetectionRules( + List.of(ENCRYPT_CHACHA20POLY1305, DECRYPT_CHACHA20POLY1305)); + + private static final Supplier>> RULES = + Memoize.of(PycaAEAD::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + return List.of(GENERATION_CHACHA20POLY1305); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pyca/aead/PycaAES.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/aead/PycaAES.java new file mode 100644 index 000000000..85e16955f --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/aead/PycaAES.java @@ -0,0 +1,109 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.aead; + +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.Size; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.context.SecretKeyContext; +import com.ibm.engine.model.factory.CipherActionFactory; +import com.ibm.engine.model.factory.KeySizeFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PycaAES { + + private PycaAES() { + // private + } + + private static final List aesAlgorithms = + Arrays.asList("AESGCM", "AESGCMIV", "AESOCB3", "AESSIV", "AESCCM"); + + private static final String AEAD_TYPE_PREFIX = "cryptography.hazmat.primitives.ciphers.aead."; + + private static @Nonnull IDetectionRule encryptAES(String aesAlgorithm) { + return new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(AEAD_TYPE_PREFIX + aesAlgorithm) + .forMethods("encrypt") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) + .withAnyParameters() + .buildForContext( + new CipherContext(Map.of("algorithm", aesAlgorithm, "kind", "AEAD"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + } + + private static @Nonnull IDetectionRule decryptAES(String aesAlgorithm) { + return new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(AEAD_TYPE_PREFIX + aesAlgorithm) + .forMethods("decrypt") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) + .withAnyParameters() + .buildForContext( + new CipherContext(Map.of("algorithm", aesAlgorithm, "kind", "AEAD"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + } + + private static @Nonnull List> generationRulesAES() { + LinkedList> rules = new LinkedList<>(); + for (String aesAlgorithm : aesAlgorithms) { + rules.add( + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(AEAD_TYPE_PREFIX + aesAlgorithm) + .forMethods("generate_key") + .withMethodParameter("int") + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) + .buildForContext( + new SecretKeyContext( + Map.of("algorithm", aesAlgorithm, "kind", "AEAD"))) + .inBundle(() -> "Pyca") + .withDependingDetectionRules( + List.of(decryptAES(aesAlgorithm), encryptAES(aesAlgorithm)))); + } + return rules; + } + + private static final Supplier>> RULES = + Memoize.of(PycaAES::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + return generationRulesAES(); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaDSA.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaDSA.java new file mode 100644 index 000000000..e53f390df --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaDSA.java @@ -0,0 +1,114 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.asymmetric; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.Size; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.model.context.PublicKeyContext; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.engine.model.factory.KeyActionFactory; +import com.ibm.engine.model.factory.KeySizeFactory; +import com.ibm.engine.model.factory.SignatureActionFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import com.ibm.plugin.rules.detection.pyca.hash.PycaHash; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PycaDSA { + + private PycaDSA() { + // nothing + } + + private static final String TYPE = "cryptography.hazmat.primitives.asymmetric.dsa"; + + private static final IDetectionRule SIGN_DSA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(TYPE + ".generate_private_key") + .forMethods("sign") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withMethodParameter(ANY) + .withMethodParameter( + "cryptography.hazmat.primitives.*") // This "type" accepts both hashes + // and pre-hashes + .addDependingDetectionRules( + PycaHash.rules()) // The parameter of sign can either be an immediate + // hash, or a hash enclosed in the pre-hash + .buildForContext(new SignatureContext(Map.of("algorithm", "DSA"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule GENERATION_DSA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(TYPE) + .forMethods("generate_private_key") + .withMethodParameter("int") + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "DSA"))) + .inBundle(() -> "Pyca") + .withDependingDetectionRules(List.of(SIGN_DSA)); + + private static final IDetectionRule PUBLIC_NUMBERS_DSA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(TYPE) + .forMethods("DSAPublicNumbers") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withAnyParameters() + .buildForContext(new PublicKeyContext(Map.of("algorithm", "DSA"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule PRIVATE_NUMBERS_DSA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(TYPE) + .forMethods("DSAPrivateNumbers") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withAnyParameters() + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "DSA"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final Supplier>> RULES = + Memoize.of(PycaDSA::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + return List.of(GENERATION_DSA, PUBLIC_NUMBERS_DSA, PRIVATE_NUMBERS_DSA); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaDiffieHellman.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaDiffieHellman.java new file mode 100644 index 000000000..cabfe6b03 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaDiffieHellman.java @@ -0,0 +1,111 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.asymmetric; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.Size; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.model.context.PublicKeyContext; +import com.ibm.engine.model.factory.KeyActionFactory; +import com.ibm.engine.model.factory.KeySizeFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PycaDiffieHellman { + + private PycaDiffieHellman() { + // private + } + + private static final String TYPE = "cryptography.hazmat.primitives.asymmetric.dh"; + + // The key size does not yet appear in PycaDiffieHellmanGenerateTestFile because + // of the TraceSymbol problem documented on the Github issue + private static final IDetectionRule GENERATE_PARAMETERS_DH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(TYPE) + .forMethods("generate_parameters") + .withMethodParameter(ANY) + .withMethodParameter("int") + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "DH"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule GENERATION_DH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(TYPE + ".generate_parameters") + .forMethods("generate_private_key") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withAnyParameters() + .buildForContext( + new PrivateKeyContext( + Map.of( + "algorithm", "DH", + "includePublicKey", "true"))) + .inBundle(() -> "Pyca") + .withDependingDetectionRules(List.of(GENERATE_PARAMETERS_DH)); + + private static final IDetectionRule PUBLIC_NUMBERS_DH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(TYPE) + .forMethods("DHPublicNumbers") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withAnyParameters() + .buildForContext(new PublicKeyContext(Map.of("algorithm", "DH"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule PRIVATE_NUMBERS_DH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(TYPE) + .forMethods("DHPrivateNumbers") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withAnyParameters() + .buildForContext(new PublicKeyContext(Map.of("algorithm", "DH"))) + .inBundle(() -> "CryptographyDiffieHellman") + .withoutDependingDetectionRules(); + + private static final Supplier>> RULES = + Memoize.of(PycaDiffieHellman::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + return List.of(GENERATION_DH, PUBLIC_NUMBERS_DH, PRIVATE_NUMBERS_DH); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaEllipticCurve.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaEllipticCurve.java new file mode 100644 index 000000000..3e1842f6f --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaEllipticCurve.java @@ -0,0 +1,159 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.asymmetric; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.context.KeyAgreementContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.model.context.PublicKeyContext; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.engine.model.factory.AlgorithmFactory; +import com.ibm.engine.model.factory.CurveFactory; +import com.ibm.engine.model.factory.KeyActionFactory; +import com.ibm.engine.model.factory.SignatureActionFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import com.ibm.plugin.rules.detection.pyca.hash.PycaHash; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PycaEllipticCurve { + + private PycaEllipticCurve() { + // private + } + + private static final String TYPE = "cryptography.hazmat.primitives.asymmetric.ec"; + private static final String GENERATE_METHOD = "generate_private_key"; + + // ECDSA is the only algorithm accepted as in the sign/verify functions (it is the only subclass + // of EllipticCurveSignatureAlgorithm) + private static final IDetectionRule ECDSA_EC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(TYPE) + .forMethods("ECDSA") + .withMethodParameter( + "cryptography.hazmat.primitives.*") // This "type" accepts both hashes + // and pre-hashes + .addDependingDetectionRules( + PycaHash.rules()) // The parameter of ECDSA can either be an immediate + // hash, or a hash enclosed in the pre-hash function + .buildForContext(new SignatureContext(Map.of("algorithm", "ECDSA"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + public static final IDetectionRule KEY_EXCHANGE_EC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(TYPE + "." + GENERATE_METHOD) + .forMethods("exchange") + .withMethodParameter(TYPE + ".*") + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .withMethodParameter(ANY) + .buildForContext(new KeyAgreementContext(Map.of("algorithm", "EC"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule SIGN_EC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(TYPE + "." + GENERATE_METHOD) + .forMethods("sign") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withMethodParameter(ANY) + .withMethodParameter(TYPE + ".*") + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .addDependingDetectionRules(List.of(ECDSA_EC)) + .buildForContext(new SignatureContext(Map.of("algorithm", "EC"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule GENERATION_EC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(TYPE) + .forMethods(GENERATE_METHOD) + .withMethodParameter(ANY) + .shouldBeDetectedAs(new CurveFactory<>()) + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "EC"))) + .inBundle(() -> "Pyca") + .withDependingDetectionRules(List.of(SIGN_EC, KEY_EXCHANGE_EC)); + + private static final IDetectionRule DERIVATION_EC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(TYPE) + .forMethods("derive_private_key") + .withMethodParameter(ANY) + .withMethodParameter(ANY) + .shouldBeDetectedAs(new CurveFactory<>()) + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "EC"))) + .inBundle(() -> "Pyca") + .withDependingDetectionRules(List.of(SIGN_EC, KEY_EXCHANGE_EC)); + + // Private numbers relies on information (the curve) given by the public key + // For now; we only use it as a depending detection rule of PRIVATE_NUMBERS_EC + private static final IDetectionRule PRIVATE_NUMBERS_EC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(TYPE) + .forMethods("EllipticCurvePrivateNumbers") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withMethodParameter(ANY) + .withMethodParameter(ANY) + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "EC"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule PUBLIC_NUMBERS_EC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(TYPE) + .forMethods("EllipticCurvePublicNumbers") + .withMethodParameter(ANY) + .withMethodParameter(ANY) + .withMethodParameter(ANY) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .buildForContext(new PublicKeyContext(Map.of("algorithm", "EC"))) + .inBundle(() -> "Pyca") + .withDependingDetectionRules(List.of(PRIVATE_NUMBERS_EC)); + + private static final Supplier>> RULES = + Memoize.of(PycaEllipticCurve::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + return List.of(GENERATION_EC, DERIVATION_EC, PUBLIC_NUMBERS_EC); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaRSA.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaRSA.java new file mode 100644 index 000000000..4d3364e26 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaRSA.java @@ -0,0 +1,204 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.asymmetric; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.Size; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.model.context.PublicKeyContext; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.engine.model.factory.CipherActionFactory; +import com.ibm.engine.model.factory.KeyActionFactory; +import com.ibm.engine.model.factory.KeySizeFactory; +import com.ibm.engine.model.factory.SignatureActionFactory; +import com.ibm.engine.model.factory.ValueActionFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import com.ibm.plugin.rules.detection.pyca.hash.PycaHash; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PycaRSA { + + private PycaRSA() { + // private + } + + private static final String PADDING_TYPE = "cryptography.hazmat.primitives.asymmetric.padding"; + private static final String HASH_TYPE = "cryptography.hazmat.primitives.*"; + private static final String RSA_TYPE = "cryptography.hazmat.primitives.asymmetric.rsa"; + + private static final IDetectionRule MGF1 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(PADDING_TYPE) + .forMethods("MGF1") + .shouldBeDetectedAs(new ValueActionFactory<>("MGF1")) + .withMethodParameter(HASH_TYPE) // This "type" accepts both hashes + // and pre-hashes + .addDependingDetectionRules(PycaHash.rules()) + .buildForContext(new SignatureContext()) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule PSS = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(PADDING_TYPE) + .forMethods("PSS") + .shouldBeDetectedAs(new ValueActionFactory<>("RSA-PSS")) + .withMethodParameter(ANY) + .addDependingDetectionRules(List.of(MGF1)) + .withMethodParameter(ANY) + .buildForContext(new SignatureContext()) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule PKCS1v15 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(PADDING_TYPE) + .forMethods("PKCS1v15") + .shouldBeDetectedAs( + new ValueActionFactory<>( + "PKCS1v15")) // this is necessary to capture something to + // trigger the translation + .withAnyParameters() + .buildForContext(new SignatureContext(Map.of("kind", "padding"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule OAEP = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(PADDING_TYPE) + .forMethods("OAEP") + .shouldBeDetectedAs(new ValueActionFactory<>("OAEP")) + .withMethodParameter(ANY) + // .shouldBeDetectedAs(new AlgorithmFactory<>()) + // .asChildOfParameterWithId(-1) + .addDependingDetectionRules(List.of(MGF1)) + .withMethodParameter(HASH_TYPE) // This "type" accepts both hashes + // and pre-hashes + .addDependingDetectionRules( + PycaHash.rules()) // The parameter of sign can either be an immediate + // hash, or a hash enclosed in the pre-hash + .withMethodParameter(ANY) + .buildForContext(new CipherContext(Map.of("kind", "padding"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule SIGN_RSA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes( + "cryptography.hazmat.primitives.asymmetric.rsa.generate_private_key") + .forMethods("sign") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withMethodParameter(ANY) + .withMethodParameter("cryptography.hazmat.primitives.asymmetric.padding.*") + .addDependingDetectionRules( + List.of( + PSS, + PKCS1v15)) // For signatures, padding can only be PSS or PKCSv15 + .withMethodParameter( + HASH_TYPE) // This "type" accepts both hashes and pre-hashes + .addDependingDetectionRules( + PycaHash.rules()) // The parameter of sign can either be an immediate + // hash, or a hash enclosed in the pre-hash + .buildForContext(new SignatureContext()) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule DECRYPT_RSA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes( + "cryptography.hazmat.primitives.asymmetric.rsa.generate_private_key") + .forMethods("decrypt") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) + .withMethodParameter(ANY) + .withMethodParameter("cryptography.hazmat.primitives.asymmetric.padding.*") + .addDependingDetectionRules( + List.of( + OAEP, + PKCS1v15)) // For encryption/decryption, padding can only be + // OAEP or PKCSv15 + .buildForContext(new CipherContext(Map.of("algorithm", "RSA"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule GENERATION_RSA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(RSA_TYPE) + .forMethods("generate_private_key") + .withMethodParameter(ANY) + .withMethodParameter("int") + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "RSA"))) + .inBundle(() -> "Pyca") + .withDependingDetectionRules(List.of(SIGN_RSA /*,VERIFY_RSA*/, DECRYPT_RSA)); + + private static final IDetectionRule PUBLIC_NUMBERS_RSA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(RSA_TYPE) + .forMethods("RSAPublicNumbers") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withAnyParameters() + .buildForContext(new PublicKeyContext(Map.of("algorithm", "RSA"))) + .inBundle(() -> "Pyca") + .withDependingDetectionRules(List.of(SIGN_RSA /*, VERIFY_RSA*/, DECRYPT_RSA)); + + private static final IDetectionRule PRIVATE_NUMBERS_RSA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(RSA_TYPE) + .forMethods("RSAPrivateNumbers") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withAnyParameters() + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "RSA"))) + .inBundle(() -> "Pyca") + .withDependingDetectionRules(List.of(SIGN_RSA /*, VERIFY_RSA*/, DECRYPT_RSA)); + + private static final Supplier>> RULES = + Memoize.of(PycaRSA::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + return List.of(GENERATION_RSA, PUBLIC_NUMBERS_RSA, PRIVATE_NUMBERS_RSA); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaSign.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaSign.java new file mode 100644 index 000000000..7e910e632 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaSign.java @@ -0,0 +1,77 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.asymmetric; + +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.model.factory.KeyActionFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PycaSign { + + private PycaSign() { + // private + } + + private static final IDetectionRule SIGN_ED25519 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes( + "cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey") + .forMethods("generate") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withoutParameters() + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "Ed25519"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule SIGN_ED448 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes( + "cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey") + .forMethods("generate") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withoutParameters() + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "Ed448"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final Supplier>> RULES = + Memoize.of(PycaSign::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + return List.of(SIGN_ED25519, SIGN_ED448); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaFernet.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaFernet.java new file mode 100644 index 000000000..4a4fcf607 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaFernet.java @@ -0,0 +1,95 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.fernet; + +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.factory.CipherActionFactory; +import com.ibm.engine.model.factory.KeyActionFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PycaFernet { + + private PycaFernet() { + // private + } + + private static @Nonnull List> encryptDecryptFernet() { + List methodNames = + List.of("encrypt", "encrypt_at_time", "decrypt", "decrypt_at_time"); + List objectNames = List.of("Fernet", "MultiFernet"); + List> rules = new LinkedList<>(); + + for (String method : methodNames) { + for (String object : objectNames) { + rules.add( + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("cryptography.fernet." + object) + .forMethods(method) + .shouldBeDetectedAs( + new CipherActionFactory<>( + method.startsWith("encrypt") + ? CipherAction.Action.ENCRYPT + : CipherAction.Action.DECRYPT)) + .withAnyParameters() + .buildForContext(new CipherContext(Map.of("algorithm", "Fernet"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules()); + } + } + return rules; + } + + private static final IDetectionRule GENERATION_FERNET = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("cryptography.fernet.Fernet") + .forMethods("generate_key") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("algorithm", "Fernet"))) + .inBundle(() -> "Pyca") + .withDependingDetectionRules(encryptDecryptFernet()); + + private static final Supplier>> RULES = + Memoize.of(PycaFernet::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + return List.of(GENERATION_FERNET); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pyca/hash/PycaHash.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/hash/PycaHash.java new file mode 100644 index 000000000..f3da3342f --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/hash/PycaHash.java @@ -0,0 +1,131 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.hash; + +import com.ibm.engine.model.context.DigestContext; +import com.ibm.engine.model.factory.AlgorithmFactory; +import com.ibm.engine.model.factory.ValueActionFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PycaHash { + + private PycaHash() { + // private + } + + @SuppressWarnings("java:S2386") + public static final List hashes = + Arrays.asList( + "SHA1", + "SHA512_224", + "SHA512_256", + "SHA224", + "SHA256", + "SHA384", + "SHA512", + "SHA3_224", + "SHA3_256", + "SHA3_384", + "SHA3_512", + "SHAKE128", + "SHAKE256", + "MD5", + "BLAKE2b", + "BLAKE2s", + "SM3"); + + private static @Nonnull List> hashesRules() { + LinkedList> rules = new LinkedList<>(); + for (final String hash : PycaHash.hashes) { + rules.add( + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("cryptography.hazmat.primitives.hashes") + .forMethods(hash) + .shouldBeDetectedAs(new ValueActionFactory<>(hash)) + .withAnyParameters() + .buildForContext(new DigestContext()) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules()); + } + return rules; + } + + private static final IDetectionRule PRE_HASH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("cryptography.hazmat.primitives.asymmetric.utils") + .forMethods("Prehashed") + .withMethodParameter("cryptography.hazmat.primitives.hashes.*") + .addDependingDetectionRules(hashesRules()) + .buildForContext(new DigestContext()) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + // Detects hashes.Hash(hashes.SHA256()) and similar direct hash-computation usages. + private static final IDetectionRule HASH_WRAPPER = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("cryptography.hazmat.primitives.hashes") + .forMethods("Hash") + .withMethodParameter("cryptography.hazmat.primitives.hashes.*") + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .buildForContext(new DigestContext()) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final Supplier>> RULES = + Memoize.of(PycaHash::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + final List> hashAndPrehashRules = new LinkedList<>(hashesRules()); + hashAndPrehashRules.add(PRE_HASH); + return hashAndPrehashRules; + } + + @Nonnull + private static final Supplier>> WRAPPER_RULES = + Memoize.of(PycaHash::wrapperRule); + + @Nonnull + public static List> wrapperRule() { + return List.of(HASH_WRAPPER); + } + + @Nonnull + public static List> wrapperRules() { + return WRAPPER_RULES.get(); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaKDF.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaKDF.java new file mode 100644 index 000000000..ae099981a --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaKDF.java @@ -0,0 +1,231 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.kdf; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.AlgorithmParameter; +import com.ibm.engine.model.Size; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.engine.model.factory.AlgorithmFactory; +import com.ibm.engine.model.factory.AlgorithmParameterFactory; +import com.ibm.engine.model.factory.KeySizeFactory; +import com.ibm.engine.model.factory.ModeFactory; +import com.ibm.engine.model.factory.ValueActionFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PycaKDF { + + private PycaKDF() { + // private + } + + private static final String HASH_TYPE = "cryptography.hazmat.primitives.hashes.*"; + private static final String KDF_TYPE_PREFIX = "cryptography.hazmat.primitives.kdf."; + + private static final IDetectionRule X963KDF = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(KDF_TYPE_PREFIX + "x963kdf") + .forMethods("X963KDF") + .withMethodParameter(HASH_TYPE) // Accepts only hashes (not pre-hashes) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .withMethodParameter("int") + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(0) + .withMethodParameter(ANY) + .buildForContext(new KeyDerivationFunctionContext(Map.of("kind", "x963"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule KBKDFCMAC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(KDF_TYPE_PREFIX + "kbkdf") + .forMethods("KBKDFCMAC") + .withMethodParameter("cryptography.hazmat.primitives.ciphers.algorithms.*") + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .withMethodParameter(ANY) + .shouldBeDetectedAs(new ModeFactory<>()) + .asChildOfParameterWithId(0) + .withMethodParameter("int") + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(0) + .withMethodParameter(ANY) + .withMethodParameter(ANY) + .withMethodParameter(ANY) + .withMethodParameter(ANY) + .withMethodParameter(ANY) + .withMethodParameter(ANY) + .buildForContext(new KeyDerivationFunctionContext(Map.of("kind", "cmac"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule KBKDFHMAC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(KDF_TYPE_PREFIX + "kbkdf") + .forMethods("KBKDFHMAC") + .withMethodParameter(HASH_TYPE) // Accepts only hashes (not pre-hashes) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .withMethodParameter(ANY) + .shouldBeDetectedAs(new ModeFactory<>()) + .asChildOfParameterWithId(0) + .withMethodParameter("int") + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(0) + .withMethodParameter(ANY) + .withMethodParameter(ANY) + .withMethodParameter(ANY) + .withMethodParameter(ANY) + .withMethodParameter(ANY) + .withMethodParameter(ANY) + .buildForContext(new KeyDerivationFunctionContext(Map.of("kind", "hmac"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule HKDF_EXPAND = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(KDF_TYPE_PREFIX + "hkdf") + .forMethods("HKDFExpand") + .withMethodParameter(HASH_TYPE) // Accepts only hashes (not pre-hashes) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .withMethodParameter("int") + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(0) + .withMethodParameter(ANY) + .buildForContext(new KeyDerivationFunctionContext(Map.of("kind", "hkdf"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule HKDF = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(KDF_TYPE_PREFIX + "hkdf") + .forMethods("HKDF") + .withMethodParameter(HASH_TYPE) // Accepts only hashes (not pre-hashes) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .withMethodParameter("int") + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(0) + .withMethodParameter(ANY) + .withMethodParameter(ANY) + .buildForContext(new KeyDerivationFunctionContext(Map.of("kind", "hkdf"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule CONCAT_KDF_HMAC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(KDF_TYPE_PREFIX + "concatkdf") + .forMethods("ConcatKDFHMAC") + .withMethodParameter(HASH_TYPE) // Accepts only hashes (not pre-hashes) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .withMethodParameter("int") + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(0) + .withMethodParameter(ANY) + .withMethodParameter(ANY) + .buildForContext(new KeyDerivationFunctionContext(Map.of("kind", "concatkdf"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule CONCAT_KDF = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(KDF_TYPE_PREFIX + "concatkdf") + .forMethods("ConcatKDFHash") + .withMethodParameter(HASH_TYPE) // Accepts only hashes (not pre-hashes) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .withMethodParameter("int") + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(0) + .withMethodParameter(ANY) + .buildForContext(new KeyDerivationFunctionContext(Map.of("kind", "concatkdf"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule SCRYPT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(KDF_TYPE_PREFIX + "scrypt") + .forMethods("Scrypt") + .shouldBeDetectedAs(new ValueActionFactory<>("Scrypt")) + .withMethodParameter(ANY) + .withMethodParameter("int") + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(0) + .withMethodParameter("int") + .withMethodParameter("int") + .withMethodParameter("int") + .buildForContext(new KeyDerivationFunctionContext()) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule PBKDF2 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(KDF_TYPE_PREFIX + "pbkdf2") + .forMethods("PBKDF2HMAC") + .withMethodParameter(HASH_TYPE) // Accepts only hashes (not pre-hashes) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .withMethodParameter("int") + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(0) + .withMethodParameter(ANY) + .withMethodParameter("int") + .shouldBeDetectedAs( + new AlgorithmParameterFactory<>(AlgorithmParameter.Kind.ITERATIONS)) + .asChildOfParameterWithId(0) + .buildForContext(new KeyDerivationFunctionContext(Map.of("kind", "pbkdf2"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final Supplier>> RULES = + Memoize.of(PycaKDF::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + return List.of( + PBKDF2, + SCRYPT, + CONCAT_KDF, + CONCAT_KDF_HMAC, + HKDF, + HKDF_EXPAND, + KBKDFHMAC, + KBKDFCMAC, + X963KDF); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pyca/keyagreement/PycaKeyAgreement.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/keyagreement/PycaKeyAgreement.java new file mode 100644 index 000000000..13d48c151 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/keyagreement/PycaKeyAgreement.java @@ -0,0 +1,76 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.keyagreement; + +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.context.KeyAgreementContext; +import com.ibm.engine.model.factory.KeyActionFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PycaKeyAgreement { + + private PycaKeyAgreement() { + // private + } + + private static final IDetectionRule GENERATION_X25519 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes( + "cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey") + .forMethods("generate") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withoutParameters() + .buildForContext(new KeyAgreementContext(Map.of("algorithm", "x25519"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule GENERATION_X448 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey") + .forMethods("generate") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withoutParameters() + .buildForContext(new KeyAgreementContext(Map.of("algorithm", "x448"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final Supplier>> RULES = + Memoize.of(PycaKeyAgreement::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + return List.of(GENERATION_X25519, GENERATION_X448); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pyca/mac/PycaMAC.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/mac/PycaMAC.java new file mode 100644 index 000000000..20b477869 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/mac/PycaMAC.java @@ -0,0 +1,91 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.mac; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.context.MacContext; +import com.ibm.engine.model.factory.AlgorithmFactory; +import com.ibm.engine.model.factory.ValueActionFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PycaMAC { + + private PycaMAC() { + // private + } + + private static final IDetectionRule NEW_CMAC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("cryptography.hazmat.primitives.cmac") + .forMethods("CMAC") + .withMethodParameter("cryptography.hazmat.primitives.ciphers.algorithms.*") + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .buildForContext(new MacContext(Map.of("kind", "cmac"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule NEW_HMAC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("cryptography.hazmat.primitives.hmac") + .forMethods("HMAC") + .withMethodParameter(ANY) + .withMethodParameter( + "cryptography.hazmat.primitives.hashes.*") // Accepts only hashes (not + // pre-hashes) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .buildForContext(new MacContext(Map.of("kind", "hmac"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule NEW_POLY1305 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("cryptography.hazmat.primitives.poly1305") + .forMethods("Poly1305") + .shouldBeDetectedAs(new ValueActionFactory<>("Poly1305")) + .withAnyParameters() + .buildForContext(new MacContext()) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final Supplier>> RULES = + Memoize.of(PycaMAC::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + return List.of(NEW_CMAC, NEW_HMAC, NEW_POLY1305); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pyca/padding/PycaPadding.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/padding/PycaPadding.java new file mode 100644 index 000000000..314b90058 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/padding/PycaPadding.java @@ -0,0 +1,103 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.padding; + +import com.ibm.engine.model.Size; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.factory.BlockSizeFactory; +import com.ibm.engine.model.factory.ValueActionFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import com.ibm.plugin.rules.detection.pyca.symmetric.PycaCipher; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PycaPadding { + + private PycaPadding() { + // private + } + + private static final List paddings = Arrays.asList("PKCS7", "ANSIX923"); + + private static @Nonnull List> newPadding() { + final LinkedList> rules = new LinkedList<>(); + + for (String padding : paddings) { + rules.add( + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("cryptography.hazmat.primitives.padding") + .forMethods(padding) + .shouldBeDetectedAs(new ValueActionFactory<>(padding)) + .withMethodParameter("int") + .shouldBeDetectedAs(new BlockSizeFactory<>(Size.UnitType.BIT)) + .asChildOfParameterWithId(0) + .buildForContext(new CipherContext(Map.of("kind", "padding"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules()); + } + // When the block size is specified using a `block_size` attribute + for (String padding : paddings) { + for (String cipherAlgorithm : PycaCipher.blockCiphers) { + rules.add( + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("cryptography.hazmat.primitives.padding") + .forMethods(padding) + .shouldBeDetectedAs(new ValueActionFactory<>(padding)) + .withMethodParameter( + "cryptography.hazmat.primitives.ciphers.algorithms." + + cipherAlgorithm + + ".block_size") + .shouldBeDetectedAs(new BlockSizeFactory<>(Size.UnitType.BIT)) + .asChildOfParameterWithId(0) + .buildForContext(new CipherContext(Map.of("kind", "padding"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules()); + } + } + return rules; + } + + // It should be better to only detect Padding when it actually gets implied (i.e. there is + // `padder.update` function call). However, it does not bring much, and creates problems + // because the type handler may not distinguish an `encryptor.update` from `padder.update`. + + private static final Supplier>> RULES = + Memoize.of(PycaPadding::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + return newPadding(); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher.java new file mode 100644 index 000000000..06b8d0b0e --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher.java @@ -0,0 +1,133 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.symmetric; + +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.factory.AlgorithmFactory; +import com.ibm.engine.model.factory.CipherActionFactory; +import com.ibm.engine.model.factory.ModeFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import com.ibm.plugin.rules.detection.pyca.padding.PycaPadding; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings({"java:S2386", "java:S1192"}) +public final class PycaCipher { + + private PycaCipher() { + // private + } + + public static final List blockCiphers = + Arrays.asList( + "AES", + "AES128", + "AES256", + "Camellia", + "TripleDES", + "CAST5", + "SEED", + "SM4", + "Blowfish", + "IDEA"); + public static final List streamCiphers = Arrays.asList("ChaCha20", "ARC4"); + + public static final List modes = + Arrays.asList("CBC", "CTR", "OFB", "CFB", "CFB8", "GCM", "XTS", "ECB"); + + private static final IDetectionRule ENCRYPT_CIPHER = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("cryptography.hazmat.primitives.ciphers.Cipher") + .forMethods("encryptor") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule DECRYPT_CIPHER = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("cryptography.hazmat.primitives.ciphers.Cipher") + .forMethods("decryptor") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static @Nonnull List> followingNewCipherRules() { + final List> encryptionRules = + new LinkedList<>(List.of(DECRYPT_CIPHER, ENCRYPT_CIPHER)); + encryptionRules.addAll(PycaPadding.rules()); + return encryptionRules; + } + + private static final IDetectionRule NEW_CIPHER = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("cryptography.hazmat.primitives.ciphers") + .forMethods("Cipher") + .withMethodParameter("cryptography.hazmat.primitives.ciphers.algorithms.*") + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .addDependingDetectionRules(followingNewCipherRules()) + .withMethodParameter("cryptography.hazmat.primitives.ciphers.modes.*") + .shouldBeDetectedAs(new ModeFactory<>()) + .asChildOfParameterWithId(0) + .buildForContext(new CipherContext()) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule STREAM_CIPHER = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("cryptography.hazmat.primitives.ciphers") + .forMethods("Cipher") + .withMethodParameter("cryptography.hazmat.primitives.ciphers.algorithms.*") + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .addDependingDetectionRules(followingNewCipherRules()) + .withMethodParameter("None") + .shouldBeDetectedAs(new ModeFactory<>()) + .asChildOfParameterWithId(0) + .buildForContext(new CipherContext()) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final Supplier>> RULES = + Memoize.of(PycaCipher::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + return List.of(NEW_CIPHER, STREAM_CIPHER); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pyca/wrapping/PycaWrapping.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/wrapping/PycaWrapping.java new file mode 100644 index 000000000..18f782847 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/wrapping/PycaWrapping.java @@ -0,0 +1,75 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.wrapping; + +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.factory.CipherActionFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PycaWrapping { + + private PycaWrapping() { + // private + } + + private static final IDetectionRule AES_KEY_WRAP = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("cryptography.hazmat.primitives.keywrap") + .forMethods("aes_key_wrap") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.WRAP)) + .withAnyParameters() + .buildForContext(new CipherContext(Map.of("algorithm", "AES"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final IDetectionRule AES_KEY_WRAP_WITH_PADDING = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("cryptography.hazmat.primitives.keywrap") + .forMethods("aes_key_wrap_with_padding") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.WRAP)) + .withAnyParameters() + .buildForContext(new CipherContext(Map.of("algorithm", "AES"))) + .inBundle(() -> "Pyca") + .withoutDependingDetectionRules(); + + private static final Supplier>> RULES = + Memoize.of(PycaWrapping::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + return List.of(AES_KEY_WRAP, AES_KEY_WRAP_WITH_PADDING); + } +} From 6162c8851bacb9ceb867c4fc0bc390a0557d00a0 Mon Sep 17 00:00:00 2001 From: san-zrl Date: Fri, 14 Aug 2026 12:36:19 +0200 Subject: [PATCH 06/13] python/pycrypto: add detection rules for PyCryptodome/PyCrypto New detection-rule classes under detection/pycrypto/: cipher - AES, DES, 3DES, Blowfish, CAST5, RC2, RC4, ChaCha20, ChaCha20-Poly1305, Salsa20, PKCS1-OAEP, PKCS1-v1.5, HPKE hash - MD2/4/5, SHA-1/2/3, RIPEMD-160, SHAKE, BLAKE2b/s, KMAC, TupleHash, cSHAKE, Keccak, KangarooTwelve, Poly1305 mac - HMAC (2-arg and 3-arg), CMAC (2-arg and 3-arg) kdf - PBKDF1 (5 arity variants), PBKDF2 (5 arity variants), scrypt, HKDF, SP800-108 keyagreement - DH.key_agreement, X25519/X448 import helpers publickey - RSA, DSA, ECC, ElGamal generate/import (RSA/DSA/ECC reached as dependent rules from Signature; only ElGamal is top-level) random - Crypto.Random.get_random_bytes, Crypto.Random.random methods signature - PKCS1v15 (emits RSA-PKCS1V15), PSS, DSS, ECDSA, EdDSA All rules use .inBundle(() -> "PyCrypto") for correct CBOM attribution. Signed-off-by: san-zrl --- .../pycrypto/cipher/PythonCryptoCipher.java | 258 ++++++++++++++ .../pycrypto/hash/PythonCryptoHash.java | 93 +++++ .../pycrypto/kdf/PythonCryptoKDF.java | 326 ++++++++++++++++++ .../PythonCryptoKeyAgreement.java | 144 ++++++++ .../pycrypto/mac/PythonCryptoMac.java | 127 +++++++ .../publickey/PythonCryptoPublicKey.java | 281 +++++++++++++++ .../pycrypto/random/PythonCryptoRandom.java | 77 +++++ .../signature/PythonCryptoSignature.java | 256 ++++++++++++++ 8 files changed, 1562 insertions(+) create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/cipher/PythonCryptoCipher.java create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/hash/PythonCryptoHash.java create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/kdf/PythonCryptoKDF.java create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/PythonCryptoKeyAgreement.java create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/mac/PythonCryptoMac.java create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/publickey/PythonCryptoPublicKey.java create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/random/PythonCryptoRandom.java create mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/signature/PythonCryptoSignature.java diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/cipher/PythonCryptoCipher.java b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/cipher/PythonCryptoCipher.java new file mode 100644 index 000000000..4efb6bd40 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/cipher/PythonCryptoCipher.java @@ -0,0 +1,258 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.factory.AlgorithmFactory; +import com.ibm.engine.model.factory.CipherActionFactory; +import com.ibm.engine.model.factory.ModeFactory; +import com.ibm.engine.model.factory.ValueActionFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import com.ibm.plugin.rules.detection.pycrypto.publickey.PythonCryptoPublicKey; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PythonCryptoCipher { + + private PythonCryptoCipher() { + // private + } + + private static final IDetectionRule ENCRYPT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(ANY) + .forMethods("encrypt", "encrypt_and_digest") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) + .withMethodParameter(ANY) + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule DECRYPT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(ANY) + .forMethods("decrypt", "decrypt_and_verify") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) + .withMethodParameter(ANY) + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule AES = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Cipher.AES", "Cryptodome.Cipher.AES") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("AES")) + .withMethodParameter(ANY) + .withMethodParameter(ANY) // Crypto.Cipher.AES.* or Cryptodome.Cipher.AES.* + .shouldBeDetectedAs(new ModeFactory<>()) + .asChildOfParameterWithId(-1) + .withOtherParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule DES = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Cipher.DES", "Cryptodome.Cipher.DES") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("DES")) + .withMethodParameter(ANY) + .withMethodParameter(ANY) // Crypto.Cipher.DES.* or Cryptodome.Cipher.DES.* + .shouldBeDetectedAs(new ModeFactory<>()) + .asChildOfParameterWithId(-1) + .withOtherParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule DES3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Cipher.DES3", "Cryptodome.Cipher.DES3") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("3DES")) + .withMethodParameter(ANY) + .withMethodParameter(ANY) // Crypto.Cipher.DES3.* or Cryptodome.Cipher.DES3.* + .shouldBeDetectedAs(new ModeFactory<>()) + .asChildOfParameterWithId(-1) + .withOtherParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule BLOWFISH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Cipher.Blowfish", "Cryptodome.Cipher.Blowfish") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("Blowfish")) + .withMethodParameter(ANY) + .withMethodParameter( + ANY) // Crypto.Cipher.Blowfish.* or Cryptodome.Cipher.Blowfish.* + .shouldBeDetectedAs(new ModeFactory<>()) + .asChildOfParameterWithId(-1) + .withOtherParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule CAST = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Cipher.CAST", "Cryptodome.Cipher.CAST") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("CAST5")) + .withMethodParameter(ANY) + .withMethodParameter(ANY) // Crypto.Cipher.CAST.* or Cryptodome.Cipher.CAST.* + .shouldBeDetectedAs(new ModeFactory<>()) + .asChildOfParameterWithId(-1) + .withOtherParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule ARC2 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Cipher.ARC2", "Cryptodome.Cipher.ARC2") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("RC2")) + .withMethodParameter(ANY) + .withMethodParameter(ANY) // Crypto.Cipher.ARC2.* or Cryptodome.Cipher.ARC2.* + .shouldBeDetectedAs(new ModeFactory<>()) + .asChildOfParameterWithId(-1) + .withOtherParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule ARC4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Cipher.ARC4", "Cryptodome.Cipher.ARC4") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("RC4")) + .withAnyParameters() + .buildForContext(new CipherContext(Map.of("algorithm", "RC4"))) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule CHACHA20 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Cipher.ChaCha20", "Cryptodome.Cipher.ChaCha20") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("ChaCha20")) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule CHACHA20_POLY1305 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes( + "Crypto.Cipher.ChaCha20_Poly1305", + "Cryptodome.Cipher.ChaCha20_Poly1305") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("ChaCha20Poly1305")) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule SALSA20 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Cipher.Salsa20", "Cryptodome.Cipher.Salsa20") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("Salsa20")) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule PKCS1_OAEP = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectExactTypes("Crypto.Cipher.PKCS1_OAEP", "Cryptodome.Cipher.PKCS1_OAEP") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("PKCS1_OAEP")) + .withMethodParameter( + ANY) // Crypto.PublicKey.RSAkey or Cryptodome.PublicKey.RSAkey + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules(PythonCryptoPublicKey.RSARules()) + .withOtherParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule PKCS1_V1_5 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectExactTypes("Crypto.Cipher.PKCS1_v1_5", "Cryptodome.Cipher.PKCS1_v1_5") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("PKCS1_v1_5")) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + @Nonnull + private static final Supplier>> RULES = + Memoize.of(PythonCryptoCipher::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + return List.of( + AES, + DES, + DES3, + BLOWFISH, + CAST, + ARC2, + ARC4, + CHACHA20, + CHACHA20_POLY1305, + SALSA20, + PKCS1_OAEP, + PKCS1_V1_5); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/hash/PythonCryptoHash.java b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/hash/PythonCryptoHash.java new file mode 100644 index 000000000..a1dd7ee58 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/hash/PythonCryptoHash.java @@ -0,0 +1,93 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import com.ibm.engine.model.context.DigestContext; +import com.ibm.engine.model.factory.ValueActionFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PythonCryptoHash { + + private PythonCryptoHash() { + // private + } + + public static final List hashes = + Arrays.asList( + "MD2", + "MD4", + "MD5", + "SHA1", + "SHA224", + "SHA256", + "SHA384", + "SHA512", + "SHA3_224", + "SHA3_256", + "SHA3_384", + "SHA3_512", + "RIPEMD160", + "keccak", + "TupleHash128", + "TupleHash256", + "SHAKE128", + "SHAKE256", + "cSHAKE128", + "cSHAKE256", + "KangarooTwelve", + "BLAKE2b", + "BLAKE2s"); + + @Nonnull + private static final Supplier>> RULES = + Memoize.of(PythonCryptoHash::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + LinkedList> rules = new LinkedList<>(); + for (final String hash : PythonCryptoHash.hashes) { + rules.add( + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Hash." + hash, "Cryptodome.Hash." + hash) + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>(hash)) + .withAnyParameters() + .buildForContext(new DigestContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules()); + } + return rules; + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/kdf/PythonCryptoKDF.java b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/kdf/PythonCryptoKDF.java new file mode 100644 index 000000000..86b9328b5 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/kdf/PythonCryptoKDF.java @@ -0,0 +1,326 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.kdf; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.Size; +import com.ibm.engine.model.Size.UnitType; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.engine.model.factory.AlgorithmFactory; +import com.ibm.engine.model.factory.IterationCountFactory; +import com.ibm.engine.model.factory.KeySizeFactory; +import com.ibm.engine.model.factory.ValueActionFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PythonCryptoKDF { + + private PythonCryptoKDF() { + // private + } + + // PBKDF1 - module function call + private static final IDetectionRule PBKDF1 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.KDF", "Cryptodome.Protocol.KDF") + .forMethods("PBKDF1") + .shouldBeDetectedAs(new ValueActionFactory<>("PBKDF1")) + .withMethodParameter(ANY) // password + .withMethodParameter(ANY) // salt + .withMethodParameter("int") // dkLen + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(-1) + .buildForContext( + new KeyDerivationFunctionContext(Map.of("kind", "pycrypto-pbkdf1"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule PBKDF1_WITH_COUNT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.KDF", "Cryptodome.Protocol.KDF") + .forMethods("PBKDF1") + .shouldBeDetectedAs(new ValueActionFactory<>("PBKDF1")) + .withMethodParameter(ANY) // password + .withMethodParameter(ANY) // salt + .withMethodParameter("int") // dkLen + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(-1) + .withMethodParameter("int") // count + .shouldBeDetectedAs(new IterationCountFactory<>()) + .asChildOfParameterWithId(-1) + .buildForContext( + new KeyDerivationFunctionContext(Map.of("kind", "pycrypto-pbkdf1"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule PBKDF1_WITH_HASH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.KDF", "Cryptodome.Protocol.KDF") + .forMethods("PBKDF1") + .shouldBeDetectedAs(new ValueActionFactory<>("PBKDF1")) + .withMethodParameter(ANY) // password + .withMethodParameter(ANY) // salt + .withMethodParameter("int") // dkLen + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(-1) + .withMethodParameter(ANY) // Crypto.Hash.* or Cryptodome.Hash.* (hashAlgo) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .buildForContext( + new KeyDerivationFunctionContext(Map.of("kind", "pycrypto-pbkdf1"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule PBKDF1_WITH_COUNT_AND_HASH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.KDF", "Cryptodome.Protocol.KDF") + .forMethods("PBKDF1") + .shouldBeDetectedAs(new ValueActionFactory<>("PBKDF1")) + .withMethodParameter(ANY) // password + .withMethodParameter(ANY) // salt + .withMethodParameter("int") // dkLen + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(-1) + .withMethodParameter("int") // count + .shouldBeDetectedAs(new IterationCountFactory<>()) + .asChildOfParameterWithId(-1) + .withMethodParameter(ANY) // Crypto.Hash.* or Cryptodome.Hash.* (hashAlgo) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .buildForContext( + new KeyDerivationFunctionContext(Map.of("kind", "pycrypto-pbkdf1"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule PBKDF1_WITH_HASH_AND_COUNT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.KDF", "Cryptodome.Protocol.KDF") + .forMethods("PBKDF1") + .shouldBeDetectedAs(new ValueActionFactory<>("PBKDF1")) + .withMethodParameter(ANY) // password + .withMethodParameter(ANY) // salt + .withMethodParameter("int") // dkLen + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(-1) + .withMethodParameter(ANY) // Crypto.Hash.* or Cryptodome.Hash.* (hashAlgo) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .withMethodParameter("int") // count + .shouldBeDetectedAs(new IterationCountFactory<>()) + .asChildOfParameterWithId(-1) + .buildForContext( + new KeyDerivationFunctionContext(Map.of("kind", "pycrypto-pbkdf1"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // PBKDF2 - module function call + private static final IDetectionRule PBKDF2 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.KDF", "Cryptodome.Protocol.KDF") + .forMethods("PBKDF2") + .shouldBeDetectedAs(new ValueActionFactory<>("PBKDF2")) + .withMethodParameter(ANY) // password + .withMethodParameter(ANY) // salt + .withMethodParameter("int") // dkLen + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(-1) + .buildForContext( + new KeyDerivationFunctionContext(Map.of("kind", "pycrypto-pbkdf2"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule PBKDF2_WITH_COUNT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.KDF", "Cryptodome.Protocol.KDF") + .forMethods("PBKDF2") + .shouldBeDetectedAs(new ValueActionFactory<>("PBKDF2")) + .withMethodParameter(ANY) // password + .withMethodParameter(ANY) // salt + .withMethodParameter("int") // dkLen + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(-1) + .withMethodParameter("int") // count + .shouldBeDetectedAs(new IterationCountFactory<>()) + .buildForContext( + new KeyDerivationFunctionContext(Map.of("kind", "pycrypto-pbkdf2"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule PBKDF2_WITH_HASH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.KDF", "Cryptodome.Protocol.KDF") + .forMethods("PBKDF2") + .shouldBeDetectedAs(new ValueActionFactory<>("PBKDF2")) + .withMethodParameter(ANY) // password + .withMethodParameter(ANY) // salt + .withMethodParameter("int") // dkLen + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(-1) + .withMethodParameter(ANY) // Crypto.Hash.* or Cryptodome.Hash.* (hashAlgo) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .buildForContext( + new KeyDerivationFunctionContext(Map.of("kind", "pycrypto-pbkdf2"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule PBKDF2_WITH_HASH_AND_COUNT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.KDF", "Cryptodome.Protocol.KDF") + .forMethods("PBKDF2") + .shouldBeDetectedAs(new ValueActionFactory<>("PBKDF2")) + .withMethodParameter(ANY) // password + .withMethodParameter(ANY) // salt + .withMethodParameter("int") // dkLen + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(-1) + .withMethodParameter(ANY) // Crypto.Hash.* or Cryptodome.Hash.* (hashAlgo) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .withMethodParameter("int") // count + .shouldBeDetectedAs(new IterationCountFactory<>()) + .asChildOfParameterWithId(-1) + .buildForContext( + new KeyDerivationFunctionContext(Map.of("kind", "pycrypto-pbkdf2"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule PBKDF2_WITH_COUNT_AND_HASH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.KDF", "Cryptodome.Protocol.KDF") + .forMethods("PBKDF2") + .shouldBeDetectedAs(new ValueActionFactory<>("PBKDF2")) + .withMethodParameter(ANY) // password + .withMethodParameter(ANY) // salt + .withMethodParameter("int") // dkLen + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(-1) + .withMethodParameter("int") // count + .shouldBeDetectedAs(new IterationCountFactory<>()) + .asChildOfParameterWithId(-1) + .withMethodParameter(ANY) // Crypto.Hash.* or Cryptodome.Hash.* (hashAlgo) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .buildForContext( + new KeyDerivationFunctionContext(Map.of("kind", "pycrypto-pbkdf2"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // scrypt - module function call + private static final IDetectionRule SCRYPT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.KDF", "Cryptodome.Protocol.KDF") + .forMethods("scrypt") + .shouldBeDetectedAs(new ValueActionFactory<>("scrypt")) + .withMethodParameter(ANY) // password + .withMethodParameter(ANY) // salt + .withMethodParameter("int") // key_len + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(-1) + .withMethodParameter("int") // N + .withMethodParameter("int") // r + .withMethodParameter("int") // p + .withOtherParameters() // num_keys + .buildForContext(new KeyDerivationFunctionContext(Map.of("kind", "scrypt"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule HKDF = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.KDF", "Cryptodome.Protocol.KDF") + .forMethods("HKDF") + .shouldBeDetectedAs(new ValueActionFactory<>("HKDF")) + .withMethodParameter(ANY) // master + .withMethodParameter("int") // keylen + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(-1) + .withMethodParameter(ANY) // salt + .withMethodParameter(ANY) // Crypto.Hash.* or Cryptodome.Hash.* (hash_mod) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .buildForContext( + new KeyDerivationFunctionContext(Map.of("kind", "pycrypto-hkdf"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // scrypt - module function call + private static final IDetectionRule SP800_108_COUNTER = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.KDF", "Cryptodome.Protocol.KDF") + .forMethods("SP800_108_Counter") + .shouldBeDetectedAs(new ValueActionFactory<>("SP800_108_Counter")) + .withMethodParameter(ANY) // master + .withMethodParameter("int") // key_len + .shouldBeDetectedAs(new KeySizeFactory<>(UnitType.BYTE)) + .asChildOfParameterWithId(-1) + .withMethodParameter(ANY) // prf + .withOtherParameters() // num_keys, label + .buildForContext(new KeyDerivationFunctionContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + @Nonnull + private static final Supplier>> RULES = + Memoize.of(PythonCryptoKDF::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + return List.of( + PBKDF1, + PBKDF1_WITH_HASH, + PBKDF1_WITH_COUNT, + PBKDF1_WITH_HASH_AND_COUNT, + PBKDF1_WITH_COUNT_AND_HASH, + PBKDF2, + PBKDF2_WITH_HASH, + PBKDF2_WITH_COUNT, + PBKDF2_WITH_HASH_AND_COUNT, + PBKDF2_WITH_COUNT_AND_HASH, + SCRYPT, + HKDF, + SP800_108_COUNTER); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/PythonCryptoKeyAgreement.java b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/PythonCryptoKeyAgreement.java new file mode 100644 index 000000000..a615a09ad --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/PythonCryptoKeyAgreement.java @@ -0,0 +1,144 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.keyagreement; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.context.KeyAgreementContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.model.context.PublicKeyContext; +import com.ibm.engine.model.factory.AlgorithmFactory; +import com.ibm.engine.model.factory.KeyActionFactory; +import com.ibm.engine.model.factory.ValueActionFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import com.ibm.plugin.rules.detection.pycrypto.publickey.PythonCryptoPublicKey; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Stream; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PythonCryptoKeyAgreement { + + private PythonCryptoKeyAgreement() { + // private + } + + private static final IDetectionRule IMPORT_X25519_PUBLIC_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.DH", "Cryptodome.Protocol.DH") + .forMethods("import_x25519_public_key") + .shouldBeDetectedAs( + new KeyActionFactory<>(KeyAction.Action.PUBLIC_KEY_GENERATION)) + .withAnyParameters() + .buildForContext(new PublicKeyContext(Map.of("algorithm", "Curve25519"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule IMPORT_X25519_PRIVATE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.DH", "Cryptodome.Protocol.DH") + .forMethods("import_x25519_private_key") + .shouldBeDetectedAs( + new KeyActionFactory<>(KeyAction.Action.PRIVATE_KEY_GENERATION)) + .withAnyParameters() + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "Curve25519"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule IMPORT_X448_PUBLIC_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.DH", "Cryptodome.Protocol.DH") + .forMethods("import_x448_public_key") + .shouldBeDetectedAs( + new KeyActionFactory<>(KeyAction.Action.PUBLIC_KEY_GENERATION)) + .withAnyParameters() + .buildForContext(new PublicKeyContext(Map.of("algorithm", "Curve448"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule IMPORT_X448_PRIVATE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.DH", "Cryptodome.Protocol.DH") + .forMethods("import_x448_private_key") + .shouldBeDetectedAs( + new KeyActionFactory<>(KeyAction.Action.PRIVATE_KEY_GENERATION)) + .withAnyParameters() + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "Curve448"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // DH.key_agreement - module function call + private static final IDetectionRule DH_KEY_AGREEMENT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.DH", "Cryptodome.Protocol.DH") + .forMethods("key_agreement") + .shouldBeDetectedAs(new ValueActionFactory<>("ECDH")) + .withMethodParameter(ANY) // kdf + .withMethodParameter( + ANY) // Crypto.PublicKey.ECC.ECCKey or Cryptodome.PublicKey.ECC.ECCKey + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules( + Stream.concat( + PythonCryptoPublicKey.ECCRules().stream(), + Stream.of( + IMPORT_X25519_PRIVATE_KEY, + IMPORT_X448_PRIVATE_KEY)) + .toList()) + .withMethodParameter( + ANY) // Crypto.PublicKey.ECC.ECCKey or Cryptodome.PublicKey.ECC.ECCKey + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules( + Stream.concat( + PythonCryptoPublicKey.ECCRules().stream(), + Stream.of( + IMPORT_X25519_PUBLIC_KEY, + IMPORT_X448_PUBLIC_KEY)) + .toList()) + .buildForContext(new KeyAgreementContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + @Nonnull + private static final Supplier>> RULES = + Memoize.of(PythonCryptoKeyAgreement::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + return List.of(DH_KEY_AGREEMENT); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/mac/PythonCryptoMac.java b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/mac/PythonCryptoMac.java new file mode 100644 index 000000000..6189c739a --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/mac/PythonCryptoMac.java @@ -0,0 +1,127 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.mac; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.context.MacContext; +import com.ibm.engine.model.factory.AlgorithmFactory; +import com.ibm.engine.model.factory.ValueActionFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PythonCryptoMac { + + private PythonCryptoMac() { + // private + } + + private static final IDetectionRule CMAC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Hash.CMAC", "Cryptodome.Hash.CMAC") + .forMethods("new") + .withMethodParameter(ANY) // key + .withMethodParameter(ANY) // Crypto.Cipher.* or Cryptodome.Cipher.* + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .buildForContext(new MacContext(Map.of("kind", "cmac"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule CMAC_MSG = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Hash.CMAC", "Cryptodome.Hash.CMAC") + .forMethods("new") + .withMethodParameter(ANY) // key + .withMethodParameter(ANY) // msg + .withMethodParameter(ANY) // Crypto.Cipher.* or Cryptodome.Cipher.* + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .withOtherParameters() + .buildForContext(new MacContext(Map.of("kind", "cmac"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule HMAC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Hash.HMAC", "Cryptodome.Hash.HMAC") + .forMethods("new") + .withMethodParameter(ANY) // secret + .withMethodParameter(ANY) // Crypto.Hash.* or Cryptodome.Hash.* + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .buildForContext(new MacContext(Map.of("kind", "hmac"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule HMAC_MSG = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Hash.HMAC", "Cryptodome.Hash.HMAC") + .forMethods("new") + .withMethodParameter(ANY) // secret + .withMethodParameter(ANY) // message + .withMethodParameter(ANY) // Crypto.Hash.* or Cryptodome.Hash.* + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .buildForContext(new MacContext(Map.of("kind", "hmac"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + public static final List simpleMACs = List.of("KMAC128", "KMAC256", "Poly1305"); + + @Nonnull + private static final Supplier>> RULES = + Memoize.of(PythonCryptoMac::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + List> rules = new ArrayList<>(); + // add CMAC + HMAC + rules.addAll(List.of(CMAC, CMAC_MSG, HMAC, HMAC_MSG)); + + // add "simple" MACs + for (final String mac : PythonCryptoMac.simpleMACs) { + rules.add( + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Hash." + mac, "Cryptodome.Hash." + mac) + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>(mac)) + .withAnyParameters() + .buildForContext(new MacContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules()); + } + return rules; + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/publickey/PythonCryptoPublicKey.java b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/publickey/PythonCryptoPublicKey.java new file mode 100644 index 000000000..9a4691b55 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/publickey/PythonCryptoPublicKey.java @@ -0,0 +1,281 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.publickey; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.Size; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.model.context.PublicKeyContext; +import com.ibm.engine.model.factory.CurveFactory; +import com.ibm.engine.model.factory.KeyActionFactory; +import com.ibm.engine.model.factory.KeySizeFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +// Only the ElGamal rules are registered as top-level detection rules. The documentation +// describes them as obsolete keys. Pycryptodome does not provide a higher-level method +// (encryption, signature) based on ElGamal keys. +// +// For RSA, DSA, and ECC keys there are corresponding signature or encryption schemes +// that use them as a parameter. In order the detect the key object in the context of +// these higher-level methods the corresponding rules are used as dependent rules. +@SuppressWarnings("java:S1192") +public final class PythonCryptoPublicKey { + + private PythonCryptoPublicKey() { + // private + } + + // RSA generate -> private key + private static final IDetectionRule RSA_GENERATE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.PublicKey.RSA", "Cryptodome.PublicKey.RSA") + .forMethods("generate") + // .shouldBeDetectedAs( + // new KeyActionFactory<>(KeyAction.Action.PRIVATE_KEY_GENERATION)) + .withMethodParameter("int") // keylen + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) + .asChildOfParameterWithId(-1) + .withOtherParameters() // randfunc, e + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "RSA"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // RSA construct and import_key -> public or private key + private static final IDetectionRule RSA_CONSTRUCT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.PublicKey.RSA", "Cryptodome.PublicKey.RSA") + .forMethods("construct", "import_key") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("algorithm", "RSA"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // RsaKey public_key -> public key + private static final IDetectionRule RSA_PUBLIC_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes( + "Crypto.PublicKey.RSA.RsaKey", "Cryptodome.PublicKey.RSA.RsaKey") + .forMethods("public_key") + .shouldBeDetectedAs( + new KeyActionFactory<>(KeyAction.Action.PUBLIC_KEY_GENERATION)) + .withoutParameters() + .buildForContext(new PublicKeyContext(Map.of("algorithm", "RSA"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // DSA generate -> private key + private static final IDetectionRule DSA_GENERATE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.PublicKey.DSA", "Cryptodome.PublicKey.DSA") + .forMethods("generate") + // .shouldBeDetectedAs( + // new KeyActionFactory<>(KeyAction.Action.PRIVATE_KEY_GENERATION)) + .withMethodParameter("int") // keylen + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) + .asChildOfParameterWithId(-1) + .withOtherParameters() // randfunc, domain + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "DSA"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // DSA construct and import_key -> public or private key + private static final IDetectionRule DSA_CONSTRUCT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.PublicKey.DSA", "Cryptodome.PublicKey.DSA") + .forMethods("construct", "import_key") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("algorithm", "DSA"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // DsaKey public_key -> public key + private static final IDetectionRule DSA_PUBLIC_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes( + "Crypto.PublicKey.DSA.DsaKey", "Cryptodome.PublicKey.DSA.DsaKey") + .forMethods("public_key") + .shouldBeDetectedAs( + new KeyActionFactory<>(KeyAction.Action.PUBLIC_KEY_GENERATION)) + .withoutParameters() + .buildForContext(new PublicKeyContext(Map.of("algorithm", "DSA"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // ECC key generation + private static final IDetectionRule ECC_GENERATE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.PublicKey.ECC", "Cryptodome.PublicKey.ECC") + .forMethods("generate") + // .shouldBeDetectedAs(new + // KeyActionFactory<>(KeyAction.Action.PRIVATE_KEY_GENERATION)) + // signature is **kwargs! + .withMethodParameter("str") // assuming curve as 1st parameter + .shouldBeDetectedAs(new CurveFactory<>()) + .asChildOfParameterWithId(-1) + .withOtherParameters() // randfunc + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "EC"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // ECC key construction + private static final IDetectionRule ECC_CONSTRUCT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.PublicKey.ECC", "Cryptodome.PublicKey.ECC") + .forMethods("construct") + // .shouldBeDetectedAs( + // new KeyActionFactory<>(KeyAction.Action.PRIVATE_KEY_GENERATION)) + .withMethodParameter("str") // curve + .shouldBeDetectedAs(new CurveFactory<>()) + .asChildOfParameterWithId(-1) + .withOtherParameters() // d, seed, point_x, point_y + .buildForContext(new KeyContext(Map.of("algorithm", "EC"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // ECC import + private static final IDetectionRule ECC_IMPORT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.PublicKey.ECC", "Cryptodome.PublicKey.ECC") + .forMethods("import_key") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("algorithm", "EC"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // EccKey public_key -> public key + private static final IDetectionRule ECC_PUBLIC_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes( + "Crypto.PublicKey.ECC.EccKey", "Cryptodome.PublicKey.ECC.EccKey") + .forMethods("public_key") + .shouldBeDetectedAs( + new KeyActionFactory<>(KeyAction.Action.PUBLIC_KEY_GENERATION)) + .withoutParameters() + .buildForContext(new PublicKeyContext(Map.of("algorithm", "EC"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // ElGamal + private static final IDetectionRule ELGAMAL_GENERATE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.PublicKey.ElGamal", "Cryptodome.PublicKey.ElGamal") + .forMethods("generate") + // .shouldBeDetectedAs( + // new KeyActionFactory<>(KeyAction.Action.PRIVATE_KEY_GENERATION)) + .withMethodParameter("int") // bits + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) + .asChildOfParameterWithId(-1) + .withMethodParameter(ANY) // randfunc + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "ElGamal"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // ElGamal + private static final IDetectionRule ELGAMAL_CONSTRUCT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.PublicKey.ElGamal", "Cryptodome.PublicKey.ElGamal") + .forMethods("construct") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("algorithm", "ElGamal"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + @Nonnull + private static final Supplier>> RSA_RULES = + Memoize.of(PythonCryptoPublicKey::buildRSARules); + + @Nonnull + public static List> RSARules() { + return RSA_RULES.get(); + } + + @Nonnull + private static List> buildRSARules() { + return List.of(RSA_CONSTRUCT, RSA_GENERATE, RSA_PUBLIC_KEY); + } + + @Nonnull + private static final Supplier>> DSA_RULES = + Memoize.of(PythonCryptoPublicKey::buildDSARules); + + @Nonnull + public static List> DSARules() { + return DSA_RULES.get(); + } + + @Nonnull + private static List> buildDSARules() { + return List.of(DSA_CONSTRUCT, DSA_GENERATE, DSA_PUBLIC_KEY); + } + + @Nonnull + private static final Supplier>> ECC_RULES = + Memoize.of(PythonCryptoPublicKey::buildECCRules); + + @Nonnull + public static List> ECCRules() { + return ECC_RULES.get(); + } + + @Nonnull + private static List> buildECCRules() { + return List.of(ECC_CONSTRUCT, ECC_GENERATE, ECC_IMPORT, ECC_PUBLIC_KEY); + } + + @Nonnull + private static final Supplier>> RULES = + Memoize.of(PythonCryptoPublicKey::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + return List.of(ELGAMAL_CONSTRUCT, ELGAMAL_GENERATE); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/random/PythonCryptoRandom.java b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/random/PythonCryptoRandom.java new file mode 100644 index 000000000..ef7de8d87 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/random/PythonCryptoRandom.java @@ -0,0 +1,77 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.random; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.context.PRNGContext; +import com.ibm.engine.model.factory.ValueActionFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import java.util.List; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PythonCryptoRandom { + + private PythonCryptoRandom() { + // private + } + + private static final IDetectionRule RANDOM_GET_BYTES = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Random", "Cryptodome.Random") + .forMethods("get_random_bytes") + .shouldBeDetectedAs(new ValueActionFactory<>("PRNG")) + .withMethodParameter(ANY) + .buildForContext(new PRNGContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule RANDOM_FUNC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Random.random", "Cryptodome.Random.random") + .forMethods( + "getrandbits", "randrange", "randint", "choice", "shuffle", "sample") + .shouldBeDetectedAs(new ValueActionFactory<>("PRNG")) + .withMethodParameter(ANY) + .buildForContext(new PRNGContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + @Nonnull + private static final Supplier>> RULES = + Memoize.of(PythonCryptoRandom::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + return List.of(RANDOM_GET_BYTES, RANDOM_FUNC); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/signature/PythonCryptoSignature.java b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/signature/PythonCryptoSignature.java new file mode 100644 index 000000000..9a426de7f --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/signature/PythonCryptoSignature.java @@ -0,0 +1,256 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.model.context.PublicKeyContext; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.engine.model.factory.AlgorithmFactory; +import com.ibm.engine.model.factory.KeyActionFactory; +import com.ibm.engine.model.factory.SignatureActionFactory; +import com.ibm.engine.model.factory.ValueActionFactory; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.Memoize; +import com.ibm.plugin.rules.detection.pycrypto.hash.PythonCryptoHash; +import com.ibm.plugin.rules.detection.pycrypto.publickey.PythonCryptoPublicKey; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Stream; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PythonCryptoSignature { + + private PythonCryptoSignature() { + // private + } + + private static final IDetectionRule SIGN = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(ANY) + .forMethods("sign") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withMethodParameter(ANY) // Crypto.Hash.* or Cryptodome.Hash.* + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules(PythonCryptoHash.rules()) + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule VERIFY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(ANY) + .forMethods("verify") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.VERIFY)) + .withMethodParameter(ANY) // Crypto.Hash.* or Cryptodome.Hash.* + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules(PythonCryptoHash.rules()) + .withMethodParameter(ANY) // the signature to be verified + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // PSS signature scheme - sign and verify methods (called on the result of .new()) + private static final IDetectionRule PKCS1V15 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Signature.pkcs1_15", "Cryptodome.Signature.pkcs1_15") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("RSA-PKCS1V15")) + .withMethodParameter(ANY) // Crypto.PublicKey.RSA or Cryptodome.PublicKey.RSA + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules(PythonCryptoPublicKey.RSARules()) + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(SIGN, VERIFY)); + + // PSS signature scheme - sign and verify methods (called on the result of .new()) + private static final IDetectionRule PSS = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Signature.pss", "Cryptodome.Signature.pss") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("RSA-PSS")) + .withMethodParameter( + ANY) // Crypto.PublicKey.RSAkey or Cryptodome.PublicKey.RSAkey + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules(PythonCryptoPublicKey.RSARules()) + .withOtherParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(SIGN, VERIFY)); + + // PSS signature scheme - sign and verify methods (called on the result of .new()) + private static final IDetectionRule PSS_MGF1 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Signature.pss", "Cryptodome.Signature.pss") + .forMethods("MGF1") + .shouldBeDetectedAs(new ValueActionFactory<>("MGF1")) + .withMethodParameter(ANY) + .withMethodParameter(ANY) + .withMethodParameter(ANY) // Crypto.Hash.* or Cryptodome.Hash.* + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(SIGN, VERIFY)); + + // DSS signature scheme - sign and verify methods (called on the result of .new()) + private static final IDetectionRule DSS = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Signature.DSS") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("DSS")) + .withMethodParameter("Crypto.PublicKey.DSA") // key + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules(PythonCryptoPublicKey.DSARules()) + .withOtherParameters() // mode, encoding, randfunc + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(SIGN, VERIFY)); + + // DSS signature scheme - sign and verify methods (called on the result of .new()) + private static final IDetectionRule DSS_CRYPTODOME = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Cryptodome.Signature.DSS") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("DSS")) + .withMethodParameter("Cryptodome.PublicKey.DSA") // key + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules(PythonCryptoPublicKey.DSARules()) + .withOtherParameters() // mode, encoding, randfunc + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(SIGN, VERIFY)); + + // DSS signature scheme - sign and verify methods (called on the result of .new()) + private static final IDetectionRule ECDSA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Signature.DSS") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("ECDSA")) + .withMethodParameter("Crypto.PublicKey.ECC") + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules(PythonCryptoPublicKey.ECCRules()) + .withOtherParameters() // mode, encoding, rand_func + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(SIGN, VERIFY)); + + // DSS signature scheme - sign and verify methods (called on the result of .new()) + private static final IDetectionRule ECDSA_CRYPTODOME = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Cryptodome.Signature.DSS") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("ECDSA")) + .withMethodParameter("Cryptodome.PublicKey.ECC") + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules(PythonCryptoPublicKey.ECCRules()) + .withOtherParameters() // mode, encoding, rand_func + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(SIGN, VERIFY)); + + // EdDSA import private key + private static final IDetectionRule EDDSA_IMPORT_PRIVATE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Signature.eddsa", "Cryptodome.Signature.eddsa") + .forMethods("import_private_key") + .shouldBeDetectedAs( + new KeyActionFactory<>(KeyAction.Action.PRIVATE_KEY_GENERATION)) + .withAnyParameters() + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "EC"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // EdDSA import public key + private static final IDetectionRule EDDSA_IMPORT_PUBLIC_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Signature.eddsa", "Cryptodome.Signature.eddsa") + .forMethods("import_public_key") + .shouldBeDetectedAs( + new KeyActionFactory<>(KeyAction.Action.PUBLIC_KEY_GENERATION)) + .withAnyParameters() + .buildForContext(new PublicKeyContext(Map.of("algorithm", "EC"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // EdDSA signature scheme - sign and verify methods (called on the result of .new()) + private static final IDetectionRule EDDSA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Signature.eddsa", "Cryptodome.Signature.eddsa") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("EDDSA")) + .withMethodParameter( + ANY) // Crypto.PublicKey.ECCkey or Cryptodome.PublicKey.ECCkey + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules( + Stream.concat( + PythonCryptoPublicKey.ECCRules().stream(), + Stream.of( + EDDSA_IMPORT_PRIVATE_KEY, + EDDSA_IMPORT_PUBLIC_KEY)) + .toList()) + .withOtherParameters() // mode, context + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(SIGN, VERIFY)); + + @Nonnull + private static final Supplier>> RULES = + Memoize.of(PythonCryptoSignature::buildRules); + + @Nonnull + public static List> rules() { + return RULES.get(); + } + + @Nonnull + private static List> buildRules() { + return List.of( + PKCS1V15, PSS, PSS_MGF1, DSS, DSS_CRYPTODOME, ECDSA, ECDSA_CRYPTODOME, EDDSA); + } +} From 980691b361207d195de14d013f368068eeaac851 Mon Sep 17 00:00:00 2001 From: san-zrl Date: Fri, 14 Aug 2026 12:37:33 +0200 Subject: [PATCH 07/13] python: wire PyCryptodome rules and new translators into the Python plugin PythonDetectionRules: register all 8 new PyCryptodome rule bundles (Hash, Mac, Random, Cipher, PublicKey, Signature, KDF, KeyAgreement). PythonTranslator: replace dead PycaSecretContextTranslator with new PycaKeyContextTranslator; wire PycaRandomContextTranslator for PRNGContext; move KeyContext handling after Private/PublicKey so more-specific contexts are checked first. PythonReorganizerRules: add four moveNodesFromUnderFunctionalityUnderParent rules for Sign/Verify under Signature and PSS. PycaKeyContextTranslator (new): handles KeyContext for RSA/DSA/EC/ElGamal/ Fernet keys; delegates to PycaKeyBasedAlgorithmMapper and PycaCurveMapper. PycaRandomContextTranslator (new): maps PRNG to PseudorandomNumberGenerator. PycaPrivateKeyContextTranslator: delegate curve mapping to PycaCurveMapper; add ElGamal and EC_IMPORT cases; use PycaKeyBasedAlgorithmMapper. PycaPublicKeyContextTranslator: add ElGamal; add Curve branch via PycaCurveMapper; use PycaKeyBasedAlgorithmMapper. PycaCipherContextTranslator: add MODE_* aliases; PKCS1_OAEP/PKCS1_v1_5/ HPKE ValueAction cases. PycaSignatureContextTranslator: add RSA-PKCS1V15, DSS, ECDSA, EDDSA cases. PycaKeyDerivationContextTranslator: add PBKDF1/PBKDF2/HKDF root nodes; IterationCount/SaltSize translation; pycrypto-pbkdf* digest pa IterationCount/SaltSize translation; pycrypto-pbkdf* digest pa Iterationn to Algorithm. Signed-off-by: san-zrl --- .../rules/detection/PythonDetectionRules.java | 46 ++++++---- .../reorganizer/PythonReorganizerRules.java | 12 +++ .../translator/PythonTranslator.java | 19 ++-- .../contexts/PycaCipherContextTranslator.java | 62 +++++++++---- .../contexts/PycaDigestContextTranslator.java | 3 +- .../PycaKeyAgreementContextTranslator.java | 9 +- .../contexts/PycaKeyContextTranslator.java | 75 ++++++++++++++++ .../PycaKeyDerivationContextTranslator.java | 37 +++++++- .../contexts/PycaMacContextTranslator.java | 10 +-- .../PycaPrivateKeyContextTranslator.java | 87 +++---------------- .../PycaPublicKeyContextTranslator.java | 34 ++++---- .../contexts/PycaRandomContextTranslator.java | 57 ++++++++++++ .../PycaSignatureContextTranslator.java | 12 +++ 13 files changed, 327 insertions(+), 136 deletions(-) create mode 100644 python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyContextTranslator.java create mode 100644 python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaRandomContextTranslator.java diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/PythonDetectionRules.java b/python/src/main/java/com/ibm/plugin/rules/detection/PythonDetectionRules.java index b6453be38..eeb3c16ce 100644 --- a/python/src/main/java/com/ibm/plugin/rules/detection/PythonDetectionRules.java +++ b/python/src/main/java/com/ibm/plugin/rules/detection/PythonDetectionRules.java @@ -20,20 +20,28 @@ package com.ibm.plugin.rules.detection; import com.ibm.engine.rule.IDetectionRule; -import com.ibm.plugin.rules.detection.aead.PycaAEAD; -import com.ibm.plugin.rules.detection.aead.PycaAES; -import com.ibm.plugin.rules.detection.asymmetric.PycaDSA; -import com.ibm.plugin.rules.detection.asymmetric.PycaDiffieHellman; -import com.ibm.plugin.rules.detection.asymmetric.PycaEllipticCurve; -import com.ibm.plugin.rules.detection.asymmetric.PycaRSA; -import com.ibm.plugin.rules.detection.asymmetric.PycaSign; -import com.ibm.plugin.rules.detection.fernet.PycaFernet; -import com.ibm.plugin.rules.detection.hash.PycaHash; -import com.ibm.plugin.rules.detection.kdf.PycaKDF; -import com.ibm.plugin.rules.detection.keyagreement.PycaKeyAgreement; -import com.ibm.plugin.rules.detection.mac.PycaMAC; -import com.ibm.plugin.rules.detection.symmetric.PycaCipher; -import com.ibm.plugin.rules.detection.wrapping.PycaWrapping; +import com.ibm.plugin.rules.detection.pyca.aead.PycaAEAD; +import com.ibm.plugin.rules.detection.pyca.aead.PycaAES; +import com.ibm.plugin.rules.detection.pyca.asymmetric.PycaDSA; +import com.ibm.plugin.rules.detection.pyca.asymmetric.PycaDiffieHellman; +import com.ibm.plugin.rules.detection.pyca.asymmetric.PycaEllipticCurve; +import com.ibm.plugin.rules.detection.pyca.asymmetric.PycaRSA; +import com.ibm.plugin.rules.detection.pyca.asymmetric.PycaSign; +import com.ibm.plugin.rules.detection.pyca.fernet.PycaFernet; +import com.ibm.plugin.rules.detection.pyca.hash.PycaHash; +import com.ibm.plugin.rules.detection.pyca.kdf.PycaKDF; +import com.ibm.plugin.rules.detection.pyca.keyagreement.PycaKeyAgreement; +import com.ibm.plugin.rules.detection.pyca.mac.PycaMAC; +import com.ibm.plugin.rules.detection.pyca.symmetric.PycaCipher; +import com.ibm.plugin.rules.detection.pyca.wrapping.PycaWrapping; +import com.ibm.plugin.rules.detection.pycrypto.cipher.PythonCryptoCipher; +import com.ibm.plugin.rules.detection.pycrypto.hash.PythonCryptoHash; +import com.ibm.plugin.rules.detection.pycrypto.kdf.PythonCryptoKDF; +import com.ibm.plugin.rules.detection.pycrypto.keyagreement.PythonCryptoKeyAgreement; +import com.ibm.plugin.rules.detection.pycrypto.mac.PythonCryptoMac; +import com.ibm.plugin.rules.detection.pycrypto.publickey.PythonCryptoPublicKey; +import com.ibm.plugin.rules.detection.pycrypto.random.PythonCryptoRandom; +import com.ibm.plugin.rules.detection.pycrypto.signature.PythonCryptoSignature; import java.util.List; import java.util.function.Supplier; import java.util.stream.Stream; @@ -70,7 +78,15 @@ private static List> buildRules() { PycaMAC.rules().stream(), PycaWrapping.rules().stream(), PycaKDF.rules().stream(), - PycaFernet.rules().stream()) + PycaFernet.rules().stream(), + PythonCryptoHash.rules().stream(), + PythonCryptoMac.rules().stream(), + PythonCryptoRandom.rules().stream(), + PythonCryptoCipher.rules().stream(), + PythonCryptoPublicKey.rules().stream(), + PythonCryptoSignature.rules().stream(), + PythonCryptoKDF.rules().stream(), + PythonCryptoKeyAgreement.rules().stream()) .flatMap(i -> i) .toList(); } diff --git a/python/src/main/java/com/ibm/plugin/translation/reorganizer/PythonReorganizerRules.java b/python/src/main/java/com/ibm/plugin/translation/reorganizer/PythonReorganizerRules.java index 6bb880f0e..af75bcbf0 100644 --- a/python/src/main/java/com/ibm/plugin/translation/reorganizer/PythonReorganizerRules.java +++ b/python/src/main/java/com/ibm/plugin/translation/reorganizer/PythonReorganizerRules.java @@ -21,9 +21,11 @@ import com.ibm.mapper.model.BlockCipher; import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.ProbabilisticSignatureScheme; import com.ibm.mapper.model.PublicKeyEncryption; import com.ibm.mapper.model.Signature; import com.ibm.mapper.model.functionality.Sign; +import com.ibm.mapper.model.functionality.Verify; import com.ibm.mapper.reorganizer.IReorganizerRule; import com.ibm.mapper.reorganizer.rules.KeyAgreementReorganizer; import com.ibm.mapper.reorganizer.rules.KeyDerivationReorganizer; @@ -42,6 +44,14 @@ private PythonReorganizerRules() { @Nonnull public static List rules() { return Stream.of( + SignatureReorganizer.moveNodesFromUnderFunctionalityUnderParent( + Verify.class, ProbabilisticSignatureScheme.class), + SignatureReorganizer.moveNodesFromUnderFunctionalityUnderParent( + Verify.class, Signature.class), + SignatureReorganizer.moveNodesFromUnderFunctionalityUnderParent( + Sign.class, ProbabilisticSignatureScheme.class), + SignatureReorganizer.moveNodesFromUnderFunctionalityUnderParent( + Sign.class, Signature.class), SignatureReorganizer.moveNodesFromUnderFunctionalityUnderNode( Sign.class, PublicKeyEncryption.class), SignatureReorganizer.moveNodesFromUnderFunctionalityUnderNode( @@ -51,6 +61,8 @@ public static List rules() { SignatureReorganizer.MAKE_RSA_TO_SIGNATURE, KeyDerivationReorganizer.moveModeFromParentToNode(BlockCipher.class), KeyDerivationReorganizer.moveModeFromParentToNode(MessageDigest.class), + KeyAgreementReorganizer.REPLACE_ECDH_WITH_X25519_WHEN_CURVE25519, + KeyAgreementReorganizer.REPLACE_ECDH_WITH_X448_WHEN_CURVE448, KeyAgreementReorganizer.MERGE_KEYAGREEMENT_WITH_PKE_UNDER_PRIVATE_KEY, PaddingReorganizer.MOVE_OAEP_UNDER_ALGORITHM) .toList(); diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/PythonTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/PythonTranslator.java index 4a22bb4a2..a349601c3 100644 --- a/python/src/main/java/com/ibm/plugin/translation/translator/PythonTranslator.java +++ b/python/src/main/java/com/ibm/plugin/translation/translator/PythonTranslator.java @@ -27,6 +27,7 @@ import com.ibm.engine.model.context.KeyContext; import com.ibm.engine.model.context.KeyDerivationFunctionContext; import com.ibm.engine.model.context.MacContext; +import com.ibm.engine.model.context.PRNGContext; import com.ibm.engine.model.context.PrivateKeyContext; import com.ibm.engine.model.context.PublicKeyContext; import com.ibm.engine.model.context.SecretKeyContext; @@ -38,11 +39,12 @@ import com.ibm.plugin.translation.translator.contexts.PycaCipherContextTranslator; import com.ibm.plugin.translation.translator.contexts.PycaDigestContextTranslator; import com.ibm.plugin.translation.translator.contexts.PycaKeyAgreementContextTranslator; +import com.ibm.plugin.translation.translator.contexts.PycaKeyContextTranslator; import com.ibm.plugin.translation.translator.contexts.PycaKeyDerivationContextTranslator; import com.ibm.plugin.translation.translator.contexts.PycaMacContextTranslator; import com.ibm.plugin.translation.translator.contexts.PycaPrivateKeyContextTranslator; import com.ibm.plugin.translation.translator.contexts.PycaPublicKeyContextTranslator; -import com.ibm.plugin.translation.translator.contexts.PycaSecretContextTranslator; +import com.ibm.plugin.translation.translator.contexts.PycaRandomContextTranslator; import com.ibm.plugin.translation.translator.contexts.PycaSecretKeyContextTranslator; import com.ibm.plugin.translation.translator.contexts.PycaSignatureContextTranslator; import java.util.List; @@ -86,11 +88,6 @@ public Optional translate( new PycaKeyDerivationContextTranslator(); return pycaKeyDerivationContextTranslator.translate( bundleIdentifier, value, detectionValueContext, detectionLocation); - } else if (detectionValueContext.is(KeyContext.class)) { - final PycaSecretContextTranslator pycaSecretContextTranslator = - new PycaSecretContextTranslator(); - return pycaSecretContextTranslator.translate( - bundleIdentifier, value, detectionValueContext, detectionLocation); } else if (detectionValueContext.is(PrivateKeyContext.class)) { final PycaPrivateKeyContextTranslator pycaPrivateKeyContextTranslator = new PycaPrivateKeyContextTranslator(); @@ -106,6 +103,11 @@ public Optional translate( new PycaPublicKeyContextTranslator(); return pycaPublicKeyContextTranslator.translate( bundleIdentifier, value, detectionValueContext, detectionLocation); + } else if (detectionValueContext.is(KeyContext.class)) { + final PycaKeyContextTranslator pycaKeyContextTranslator = + new PycaKeyContextTranslator(); + return pycaKeyContextTranslator.translate( + bundleIdentifier, value, detectionValueContext, detectionLocation); } else if (detectionValueContext.is(DigestContext.class)) { final PycaDigestContextTranslator pycaDigestContextTranslator = new PycaDigestContextTranslator(); @@ -126,6 +128,11 @@ public Optional translate( new PycaMacContextTranslator(); return pycaMacContextTranslator.translate( bundleIdentifier, value, detectionValueContext, detectionLocation); + } else if (detectionValueContext.is(PRNGContext.class)) { + final PycaRandomContextTranslator pycaRandomContextTranslator = + new PycaRandomContextTranslator(); + return pycaRandomContextTranslator.translate( + bundleIdentifier, value, detectionValueContext, detectionLocation); } return Optional.empty(); } diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaCipherContextTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaCipherContextTranslator.java index eec44d2aa..226148286 100644 --- a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaCipherContextTranslator.java +++ b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaCipherContextTranslator.java @@ -19,6 +19,7 @@ */ package com.ibm.plugin.translation.translator.contexts; +import com.ibm.engine.model.Algorithm; import com.ibm.engine.model.CipherAction; import com.ibm.engine.model.IValue; import com.ibm.engine.model.KeySize; @@ -30,10 +31,13 @@ import com.ibm.mapper.IContextTranslation; import com.ibm.mapper.mapper.pyca.PycaCipherMapper; import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.EllipticCurveAlgorithm; import com.ibm.mapper.model.INode; import com.ibm.mapper.model.KeyLength; import com.ibm.mapper.model.KeyWrap; +import com.ibm.mapper.model.PublicKeyEncryption; import com.ibm.mapper.model.algorithms.AES; +import com.ibm.mapper.model.algorithms.RSA; import com.ibm.mapper.model.functionality.Decrypt; import com.ibm.mapper.model.functionality.Encapsulate; import com.ibm.mapper.model.functionality.Encrypt; @@ -41,15 +45,19 @@ import com.ibm.mapper.model.mode.CCM; import com.ibm.mapper.model.mode.CFB; import com.ibm.mapper.model.mode.CTR; +import com.ibm.mapper.model.mode.EAX; import com.ibm.mapper.model.mode.ECB; import com.ibm.mapper.model.mode.GCM; import com.ibm.mapper.model.mode.GCMSIV; +import com.ibm.mapper.model.mode.KW; +import com.ibm.mapper.model.mode.KWP; import com.ibm.mapper.model.mode.OCB; import com.ibm.mapper.model.mode.OFB; import com.ibm.mapper.model.mode.SIV; import com.ibm.mapper.model.mode.XTS; import com.ibm.mapper.model.padding.ANSIX923; import com.ibm.mapper.model.padding.OAEP; +import com.ibm.mapper.model.padding.PKCS1; import com.ibm.mapper.model.padding.PKCS7; import com.ibm.mapper.utils.DetectionLocation; import java.util.Optional; @@ -66,7 +74,7 @@ public final class PycaCipherContextTranslator implements IContextTranslation + if (value instanceof Algorithm && detectionContext instanceof DetectionContext context) { if (context.get("kind").map(k -> k.equals("AEAD")).orElse(false)) { return switch (value.asString().toUpperCase().trim()) { @@ -85,25 +93,49 @@ public final class PycaCipherContextTranslator implements IContextTranslation i); } else if (value instanceof ValueAction - && detectionContext instanceof DetectionContext context - && context.get("kind").map(k -> k.equals("padding")).orElse(false) // padding case - ) { - return switch (value.asString().toUpperCase().trim()) { - case "PKCS7" -> Optional.of(new PKCS7(detectionLocation)); - case "ANSIX923" -> Optional.of(new ANSIX923(detectionLocation)); - case "OAEP" -> Optional.of(new OAEP(detectionLocation)); - default -> Optional.empty(); + && detectionContext instanceof DetectionContext context) { + if (context.get("kind").map(k -> k.equals("padding")).orElse(false)) { // padding case + return switch (value.asString().toUpperCase().trim()) { + case "PKCS7" -> Optional.of(new PKCS7(detectionLocation)); + case "ANSIX923" -> Optional.of(new ANSIX923(detectionLocation)); + case "OAEP" -> Optional.of(new OAEP(detectionLocation)); + default -> Optional.empty(); + }; + } + + // Handle ValueAction with algorithm name (e.g., "AES", "DES", "DES3") + // Get the algorithm name from the ValueAction + String algorithmName = value.asString(); + return switch (algorithmName.trim().toUpperCase()) { + case "PKCS1_OAEP" -> { + RSA rsaOaep = new RSA(PublicKeyEncryption.class, detectionLocation); + rsaOaep.put(new OAEP(detectionLocation)); + yield Optional.of((INode) rsaOaep); + } + case "PKCS1_V1_5" -> { + RSA rsaPkcs1 = new RSA(PublicKeyEncryption.class, detectionLocation); + rsaPkcs1.put(new PKCS1(detectionLocation)); + yield Optional.of((INode) rsaPkcs1); + } + case "HPKE" -> Optional.of(new EllipticCurveAlgorithm(detectionLocation)); + default -> pycaCipherMapper.parse(algorithmName, detectionLocation).map(i -> i); }; } else if (value instanceof Mode mode) { return switch (mode.asString().toUpperCase().trim()) { - case "CBC" -> Optional.of(new CBC(detectionLocation)); - case "CTR" -> Optional.of(new CTR(detectionLocation)); - case "OFB" -> Optional.of(new OFB(detectionLocation)); - case "CFB" -> Optional.of(new CFB(detectionLocation)); + case "CBC", "MODE_CBC" -> Optional.of(new CBC(detectionLocation)); + case "CTR", "MODE_CTR" -> Optional.of(new CTR(detectionLocation)); + case "OFB", "MODE_OFB" -> Optional.of(new OFB(detectionLocation)); + case "MODE_OCB" -> Optional.of(new OCB(detectionLocation)); + case "CFB", "MODE_CFB" -> Optional.of(new CFB(detectionLocation)); case "CFB8" -> Optional.of(new CFB(8, detectionLocation)); - case "GCM" -> Optional.of(new GCM(detectionLocation)); + case "GCM", "MODE_GCM" -> Optional.of(new GCM(detectionLocation)); case "XTS" -> Optional.of(new XTS(detectionLocation)); - case "ECB" -> Optional.of(new ECB(detectionLocation)); + case "ECB", "MODE_ECB" -> Optional.of(new ECB(detectionLocation)); + case "MODE_EAX" -> Optional.of(new EAX(detectionLocation)); + case "CCM", "MODE_CCM" -> Optional.of(new CCM(detectionLocation)); + case "MODE_SIV" -> Optional.of(new SIV(detectionLocation)); + case "MODE_KW" -> Optional.of(new KW(detectionLocation)); + case "MODE_KWP" -> Optional.of(new KWP(detectionLocation)); default -> Optional.empty(); }; } else if (value instanceof CipherAction cipherAction diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaDigestContextTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaDigestContextTranslator.java index face8bc21..6333c5cc8 100644 --- a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaDigestContextTranslator.java +++ b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaDigestContextTranslator.java @@ -19,6 +19,7 @@ */ package com.ibm.plugin.translation.translator.contexts; +import com.ibm.engine.model.Algorithm; import com.ibm.engine.model.IValue; import com.ibm.engine.model.ValueAction; import com.ibm.engine.model.context.IDetectionContext; @@ -41,7 +42,7 @@ public final class PycaDigestContextTranslator implements IContextTranslation value, @Nonnull IDetectionContext detectionContext, @Nonnull DetectionLocation detectionLocation) { - if (value instanceof ValueAction || value instanceof com.ibm.engine.model.Algorithm) { + if (value instanceof ValueAction || value instanceof Algorithm) { final PycaDigestMapper pycaDigestMapper = new PycaDigestMapper(); return pycaDigestMapper .parse(value.asString(), detectionLocation) diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyAgreementContextTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyAgreementContextTranslator.java index 89b68213b..0407e5879 100644 --- a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyAgreementContextTranslator.java +++ b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyAgreementContextTranslator.java @@ -22,6 +22,7 @@ import com.ibm.engine.model.Algorithm; import com.ibm.engine.model.IValue; import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.ValueAction; import com.ibm.engine.model.context.DetectionContext; import com.ibm.engine.model.context.IDetectionContext; import com.ibm.engine.rule.IBundle; @@ -46,17 +47,19 @@ public class PycaKeyAgreementContextTranslator implements IContextTranslation value, @Nonnull IDetectionContext detectionContext, @Nonnull DetectionLocation detectionLocation) { - if (value instanceof Algorithm algorithm) { - return Optional.of(algorithm) + if (value instanceof ValueAction || value instanceof Algorithm) { + return Optional.of(value.asString().toUpperCase().trim()) .map( algo -> - switch (algo.asString().toUpperCase().trim()) { + switch (algo) { case "ECDH" -> new ECDH(detectionLocation); case "EC" -> new EllipticCurveAlgorithm( KeyAgreement.class, new EllipticCurveAlgorithm( detectionLocation)); + case "X25519" -> new X25519(detectionLocation); + case "X448" -> new X448(detectionLocation); default -> null; }) .map( diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyContextTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyContextTranslator.java new file mode 100644 index 000000000..396f8efd1 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyContextTranslator.java @@ -0,0 +1,75 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.translation.translator.contexts; + +import com.ibm.engine.model.Curve; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.context.DetectionContext; +import com.ibm.engine.model.context.IDetectionContext; +import com.ibm.engine.rule.IBundle; +import com.ibm.mapper.IContextTranslation; +import com.ibm.mapper.mapper.pyca.PycaCurveMapper; +import com.ibm.mapper.mapper.pyca.PycaKeyBasedAlgorithmMapper; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.utils.DetectionLocation; +import java.util.Optional; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +public final class PycaKeyContextTranslator implements IContextTranslation { + @Override + public @Nonnull Optional translate( + @Nonnull IBundle bundleIdentifier, + @Nonnull IValue value, + @Nonnull IDetectionContext detectionContext, + @Nonnull DetectionLocation detectionLocation) { + if (value instanceof KeyAction + && detectionContext instanceof DetectionContext context) { + // action is always "generate" + final PycaKeyBasedAlgorithmMapper mapper = new PycaKeyBasedAlgorithmMapper(); + return context.get("algorithm") + .flatMap(str -> mapper.parse(str, detectionLocation)) + .map( + algo -> { + final Key key = new Key(algo); + key.put(new KeyGeneration(detectionLocation)); + return key; + }); + } else if (value instanceof Curve curve + && detectionContext instanceof DetectionContext context + && context.get("algorithm").map(a -> a.equalsIgnoreCase("EC")).orElse(false)) { + final PycaCurveMapper mapper = new PycaCurveMapper(); + return mapper.parse(curve.asString(), detectionLocation) + .map( + ec -> { + Key key = new Key(ec); + key.put(new KeyGeneration(detectionLocation)); + // currently only GENERATE is + // used as key action is this + // context + return key; + }); + } + return Optional.empty(); + } +} diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyDerivationContextTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyDerivationContextTranslator.java index 72ed5115d..951c8847a 100644 --- a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyDerivationContextTranslator.java +++ b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyDerivationContextTranslator.java @@ -21,8 +21,10 @@ import com.ibm.engine.model.Algorithm; import com.ibm.engine.model.IValue; +import com.ibm.engine.model.IterationCount; import com.ibm.engine.model.KeySize; import com.ibm.engine.model.Mode; +import com.ibm.engine.model.SaltSize; import com.ibm.engine.model.ValueAction; import com.ibm.engine.model.context.DetectionContext; import com.ibm.engine.model.context.IDetectionContext; @@ -33,11 +35,15 @@ import com.ibm.mapper.model.Cipher; import com.ibm.mapper.model.INode; import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.NumberOfIterations; +import com.ibm.mapper.model.SaltLength; import com.ibm.mapper.model.algorithms.ANSIX963; import com.ibm.mapper.model.algorithms.CMAC; import com.ibm.mapper.model.algorithms.ConcatenationKDF; import com.ibm.mapper.model.algorithms.HKDF; import com.ibm.mapper.model.algorithms.HMAC; +import com.ibm.mapper.model.algorithms.KDFCounter; +import com.ibm.mapper.model.algorithms.PBKDF1; import com.ibm.mapper.model.algorithms.PBKDF2; import com.ibm.mapper.model.algorithms.Scrypt; import com.ibm.mapper.model.functionality.KeyDerivation; @@ -101,6 +107,17 @@ public class PycaKeyDerivationContextTranslator implements IContextTranslation { + final PycaDigestMapper digestMapper = new PycaDigestMapper(); + yield digestMapper + .parse(algorithm.asString(), detectionLocation) + .map( + kdf -> { + final PBKDF1 pbkdf1 = new PBKDF1(kdf); + pbkdf1.put(new KeyDerivation(detectionLocation)); + return pbkdf1; + }); + } case "pbkdf2" -> { final PycaDigestMapper digestMapper = new PycaDigestMapper(); yield digestMapper @@ -112,6 +129,14 @@ public class PycaKeyDerivationContextTranslator implements IContextTranslation { + // ValueAction already creates the root KDF node for these rules; + // the Algorithm child should contribute only the digest. + final PycaDigestMapper digestMapper = new PycaDigestMapper(); + yield digestMapper + .parse(algorithm.asString(), detectionLocation) + .map(digest -> (INode) digest); + } case "concatkdf" -> { final PycaDigestMapper digestMapper = new PycaDigestMapper(); yield digestMapper @@ -146,12 +171,22 @@ public class PycaKeyDerivationContextTranslator implements IContextTranslation keySize) { return Optional.of(new KeyLength(keySize.getValue(), detectionLocation)); + } else if (value instanceof IterationCount iterationCount) { + return Optional.of( + new NumberOfIterations(iterationCount.getValue(), detectionLocation)); + } else if (value instanceof SaltSize saltSize) { + return Optional.of(new SaltLength(saltSize.getValue(), detectionLocation)); } else if (value instanceof ValueAction action) { return Optional.of(action.asString().toUpperCase().trim()) .map( str -> - switch (action.asString().toUpperCase().trim()) { + switch (str) { + case "PBKDF1" -> new PBKDF1(detectionLocation); + case "PBKDF2" -> new PBKDF2(detectionLocation); + case "HKDF" -> new HKDF(detectionLocation); case "SCRYPT" -> new Scrypt(detectionLocation); + case "SP800_108_COUNTER" -> + new KDFCounter(detectionLocation); default -> null; }) .map( diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaMacContextTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaMacContextTranslator.java index 8f9e14618..8ad9f8ec0 100644 --- a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaMacContextTranslator.java +++ b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaMacContextTranslator.java @@ -19,6 +19,7 @@ */ package com.ibm.plugin.translation.translator.contexts; +import com.ibm.engine.model.Algorithm; import com.ibm.engine.model.IValue; import com.ibm.engine.model.ValueAction; import com.ibm.engine.model.context.DetectionContext; @@ -27,11 +28,11 @@ import com.ibm.mapper.IContextTranslation; import com.ibm.mapper.mapper.pyca.PycaCipherMapper; import com.ibm.mapper.mapper.pyca.PycaDigestMapper; +import com.ibm.mapper.mapper.pyca.PycaMacMapper; import com.ibm.mapper.model.Cipher; import com.ibm.mapper.model.INode; import com.ibm.mapper.model.algorithms.CMAC; import com.ibm.mapper.model.algorithms.HMAC; -import com.ibm.mapper.model.algorithms.Poly1305; import com.ibm.mapper.utils.DetectionLocation; import java.util.Optional; import javax.annotation.Nonnull; @@ -47,7 +48,7 @@ public final class PycaMacContextTranslator implements IContextTranslation @Nonnull IDetectionContext detectionContext, @Nonnull DetectionLocation detectionLocation) { - if (value instanceof com.ibm.engine.model.Algorithm algorithm + if (value instanceof Algorithm algorithm && detectionContext instanceof DetectionContext context) { // hash algorithm Optional possibleKind = context.get("kind"); @@ -76,9 +77,8 @@ public final class PycaMacContextTranslator implements IContextTranslation }; } } else if (value instanceof ValueAction action) { - if (action.asString().equalsIgnoreCase("poly1305")) { - return Optional.of(new HMAC(new Poly1305(detectionLocation))); - } + final PycaMacMapper macMapper = new PycaMacMapper(); + return macMapper.parse(action.asString(), detectionLocation).map(n -> n); } return Optional.empty(); } diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaPrivateKeyContextTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaPrivateKeyContextTranslator.java index b82629bd7..1b5c084f8 100644 --- a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaPrivateKeyContextTranslator.java +++ b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaPrivateKeyContextTranslator.java @@ -27,35 +27,13 @@ import com.ibm.engine.model.context.IDetectionContext; import com.ibm.engine.rule.IBundle; import com.ibm.mapper.IContextTranslation; -import com.ibm.mapper.model.EllipticCurveAlgorithm; +import com.ibm.mapper.mapper.pyca.PycaCurveMapper; +import com.ibm.mapper.mapper.pyca.PycaKeyBasedAlgorithmMapper; import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; import com.ibm.mapper.model.KeyLength; import com.ibm.mapper.model.PrivateKey; import com.ibm.mapper.model.PublicKeyEncryption; -import com.ibm.mapper.model.algorithms.DH; -import com.ibm.mapper.model.algorithms.DSA; -import com.ibm.mapper.model.algorithms.Ed25519; -import com.ibm.mapper.model.algorithms.Ed448; -import com.ibm.mapper.model.algorithms.RSA; -import com.ibm.mapper.model.curves.Brainpoolp256r1; -import com.ibm.mapper.model.curves.Brainpoolp384r1; -import com.ibm.mapper.model.curves.Brainpoolp512r1; -import com.ibm.mapper.model.curves.Secp192r1; -import com.ibm.mapper.model.curves.Secp224r1; -import com.ibm.mapper.model.curves.Secp256k1; -import com.ibm.mapper.model.curves.Secp256r1; -import com.ibm.mapper.model.curves.Secp384r1; -import com.ibm.mapper.model.curves.Secp521r1; -import com.ibm.mapper.model.curves.Sect163k1; -import com.ibm.mapper.model.curves.Sect163r2; -import com.ibm.mapper.model.curves.Sect233k1; -import com.ibm.mapper.model.curves.Sect233r1; -import com.ibm.mapper.model.curves.Sect283k1; -import com.ibm.mapper.model.curves.Sect283r1; -import com.ibm.mapper.model.curves.Sect409k1; -import com.ibm.mapper.model.curves.Sect409r1; -import com.ibm.mapper.model.curves.Sect571k1; -import com.ibm.mapper.model.curves.Sect571r1; import com.ibm.mapper.model.functionality.KeyGeneration; import com.ibm.mapper.utils.DetectionLocation; import java.util.Optional; @@ -84,41 +62,13 @@ public final class PycaPrivateKeyContextTranslator implements IContextTranslatio } else if (value instanceof Curve curve && detectionContext instanceof DetectionContext context && context.get("algorithm").map(a -> a.equalsIgnoreCase("EC")).orElse(false)) { - return Optional.of(curve.asString()) - .map( - str -> - switch (str.toUpperCase().trim()) { - case "SECP256R1" -> new Secp256r1(detectionLocation); - case "SECP384R1" -> new Secp384r1(detectionLocation); - case "SECP521R1" -> new Secp521r1(detectionLocation); - case "SECP224R1" -> new Secp224r1(detectionLocation); - case "SECP192R1" -> new Secp192r1(detectionLocation); - case "SECP256K1" -> new Secp256k1(detectionLocation); - case "BRAINPOOLP256R1" -> - new Brainpoolp256r1(detectionLocation); - case "BRAINPOOLP384R1" -> - new Brainpoolp384r1(detectionLocation); - case "BRAINPOOLP512R1" -> - new Brainpoolp512r1(detectionLocation); - case "SECT571K1" -> new Sect571k1(detectionLocation); - case "SECT409K1" -> new Sect409k1(detectionLocation); - case "SECT283K1" -> new Sect283k1(detectionLocation); - case "SECT233K1" -> new Sect233k1(detectionLocation); - case "SECT163K1" -> new Sect163k1(detectionLocation); - case "SECT571R1" -> new Sect571r1(detectionLocation); - case "SECT409R1" -> new Sect409r1(detectionLocation); - case "SECT283R1" -> new Sect283r1(detectionLocation); - case "SECT233R1" -> new Sect233r1(detectionLocation); - case "SECT163R2" -> new Sect163r2(detectionLocation); - default -> null; - }) - .map(EllipticCurveAlgorithm::new) + final PycaCurveMapper mapper = new PycaCurveMapper(); + return mapper.parse(curve.asString(), detectionLocation) .map( ec -> { PrivateKey privateKey = new PrivateKey((PublicKeyEncryption) ec); - privateKey.put( - new KeyGeneration( - detectionLocation)); // currently only GENERATE is + privateKey.put(new KeyGeneration(detectionLocation)); + // currently only GENERATE is // used as key action is this // context return privateKey; @@ -131,26 +81,13 @@ public final class PycaPrivateKeyContextTranslator implements IContextTranslatio @Nonnull DetectionContext context, @Nullable Integer keySize, @Nonnull DetectionLocation detectionLocation) { + final PycaKeyBasedAlgorithmMapper mapper = new PycaKeyBasedAlgorithmMapper(); return context.get("algorithm") + .flatMap(str -> mapper.parse(str, detectionLocation)) + .map(algorithm -> new PrivateKey(new Key(algorithm))) .map( - str -> - switch (str.toUpperCase().trim()) { - case "DH" -> new DH(detectionLocation); - case "RSA" -> new RSA(detectionLocation); - case "DSA" -> new DSA(detectionLocation); - case "EC" -> new EllipticCurveAlgorithm(detectionLocation); - case "ED25519" -> new Ed25519(detectionLocation); - case "ED448" -> new Ed448(detectionLocation); - default -> null; - }) - .map( - algorithm -> { - PrivateKey privateKey = new PrivateKey(algorithm); - privateKey.put( - new KeyGeneration( - detectionLocation)); // currently only GENERATE is - // used as key action is this - // context + privateKey -> { + privateKey.put(new KeyGeneration(detectionLocation)); return privateKey; }) .map( diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaPublicKeyContextTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaPublicKeyContextTranslator.java index b0f14a52e..95245c481 100644 --- a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaPublicKeyContextTranslator.java +++ b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaPublicKeyContextTranslator.java @@ -19,17 +19,19 @@ */ package com.ibm.plugin.translation.translator.contexts; +import com.ibm.engine.model.Curve; import com.ibm.engine.model.IValue; import com.ibm.engine.model.KeyAction; import com.ibm.engine.model.context.DetectionContext; import com.ibm.engine.model.context.IDetectionContext; import com.ibm.engine.rule.IBundle; import com.ibm.mapper.IContextTranslation; +import com.ibm.mapper.mapper.pyca.PycaCurveMapper; +import com.ibm.mapper.mapper.pyca.PycaKeyBasedAlgorithmMapper; import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; import com.ibm.mapper.model.PublicKey; -import com.ibm.mapper.model.algorithms.DH; -import com.ibm.mapper.model.algorithms.DSA; -import com.ibm.mapper.model.algorithms.RSA; +import com.ibm.mapper.model.PublicKeyEncryption; import com.ibm.mapper.model.functionality.KeyGeneration; import com.ibm.mapper.utils.DetectionLocation; import java.util.Optional; @@ -47,23 +49,25 @@ public final class PycaPublicKeyContextTranslator implements IContextTranslation @Nonnull DetectionLocation detectionLocation) { if (value instanceof KeyAction && detectionContext instanceof DetectionContext context) { + final PycaKeyBasedAlgorithmMapper mapper = new PycaKeyBasedAlgorithmMapper(); return context.get("algorithm") + .flatMap(str -> mapper.parse(str, detectionLocation)) + .map(algorithm -> new PublicKey(new Key(algorithm))) .map( - algorithm -> - switch (algorithm.toUpperCase().trim()) { - case "DH" -> new DH(detectionLocation); - case "RSA" -> new RSA(detectionLocation); - case "DSA" -> new DSA(detectionLocation); - default -> null; - }) + publicKey -> { + publicKey.put(new KeyGeneration(detectionLocation)); + return publicKey; + }); + } else if (value instanceof Curve) { + final PycaCurveMapper mapper = new PycaCurveMapper(); + return mapper.parse(value.asString(), detectionLocation) .map( - algorithm -> { - PublicKey publicKey = new PublicKey(algorithm); - publicKey.put( - new KeyGeneration( - detectionLocation)); // currently only GENERATE is + algo -> { + PublicKey publicKey = new PublicKey((PublicKeyEncryption) algo); + // currently only GENERATE is // used as key action is this // context + publicKey.put(new KeyGeneration(detectionLocation)); return publicKey; }); } diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaRandomContextTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaRandomContextTranslator.java new file mode 100644 index 000000000..4cd66fb3d --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaRandomContextTranslator.java @@ -0,0 +1,57 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.translation.translator.contexts; + +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.IDetectionContext; +import com.ibm.engine.rule.IBundle; +import com.ibm.mapper.IContextTranslation; +import com.ibm.mapper.model.Algorithm; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.PseudorandomNumberGenerator; +import com.ibm.mapper.utils.DetectionLocation; +import java.util.Optional; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1301") +public final class PycaRandomContextTranslator implements IContextTranslation { + + @Override + public @Nonnull Optional translate( + @Nonnull IBundle bundleIdentifier, + @Nonnull IValue value, + @Nonnull IDetectionContext detectionContext, + @Nonnull DetectionLocation detectionLocation) { + if (value instanceof ValueAction) { + return switch (value.asString().toUpperCase().trim()) { + case "PRNG" -> + Optional.of( + new Algorithm( + "PRNG", + PseudorandomNumberGenerator.class, + detectionLocation)); + default -> Optional.empty(); + }; + } + return Optional.empty(); + } +} diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaSignatureContextTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaSignatureContextTranslator.java index 363c68378..3ac9d725b 100644 --- a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaSignatureContextTranslator.java +++ b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaSignatureContextTranslator.java @@ -30,12 +30,15 @@ import com.ibm.mapper.model.INode; import com.ibm.mapper.model.ProbabilisticSignatureScheme; import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.algorithms.DSS; import com.ibm.mapper.model.algorithms.ECDSA; +import com.ibm.mapper.model.algorithms.EdDSA; import com.ibm.mapper.model.algorithms.MGF1; import com.ibm.mapper.model.algorithms.RSA; import com.ibm.mapper.model.algorithms.RSAssaPSS; import com.ibm.mapper.model.functionality.Sign; import com.ibm.mapper.model.functionality.Verify; +import com.ibm.mapper.model.padding.PKCS1; import com.ibm.mapper.utils.DetectionLocation; import java.util.Optional; import javax.annotation.Nonnull; @@ -82,7 +85,16 @@ public final class PycaSignatureContextTranslator implements IContextTranslation }; } else { return switch (value.asString().toUpperCase().trim()) { + case "RSA" -> Optional.of(new RSA(Signature.class, detectionLocation)); + case "DSS" -> Optional.of(new DSS(detectionLocation)); + case "ECDSA" -> Optional.of(new ECDSA(detectionLocation)); + case "EDDSA" -> Optional.of(new EdDSA(detectionLocation)); case "MGF1" -> Optional.of(new MGF1(detectionLocation)); + case "RSA-PKCS1V15" -> { + RSA rsaPkcs1 = new RSA(Signature.class, detectionLocation); + rsaPkcs1.put(new PKCS1(detectionLocation)); + yield Optional.of((INode) rsaPkcs1); + } case "RSA-PSS" -> Optional.of(new RSAssaPSS(detectionLocation)); default -> Optional.empty(); }; From 07e57b471b9eed7a38e76b37033f98d858655821 Mon Sep 17 00:00:00 2001 From: san-zrl Date: Fri, 14 Aug 2026 12:38:19 +0200 Subject: [PATCH 08/13] python/pyca: move pyca test fixtures and tests into pyca/ sub-directory Mirror the production-code reorganisation. No assertion changes; only package declarations and fixture paths updated. Signed-off-by: san-zrl --- .../detection/pyca/aead/PycaAESGCMTestFile.py | 10 + .../pyca/aead/PycaChaCha20Poly1305TestFile.py | 11 + .../asymmetric/DSA/PycaDSANumbersTestFile.py | 13 + .../asymmetric/DSA/PycaDSASignTestFile.py | 11 + .../PycaDiffieHellmanGenerateTestFile.py | 6 + .../PycaDiffieHellmanNumbersTestFile.py | 13 + .../PycaEllipticCurveDeriveTestFile.py | 12 + .../PycaEllipticCurveKeyExchangeTestFile.py | 21 ++ .../PycaEllipticCurveNumbersTestFile.py | 53 ++++ .../PycaEllipticCurveSign2TestFile.py | 48 ++++ .../PycaEllipticCurveSignTestFile.py | 37 +++ .../PycaEllipticCurveVerifyTestFile.py | 8 + .../asymmetric/RSA/PycaRSADecryptTestFile.py | 19 ++ .../asymmetric/RSA/PycaRSANumbersTestFile.py | 13 + .../asymmetric/RSA/PycaRSASign1TestFile.py | 19 ++ .../asymmetric/RSA/PycaRSASign2TestFile.py | 16 ++ .../pyca/fernet/PycaFernetDecryptTestFile.py | 15 ++ .../pyca/fernet/PycaFernetEncryptTestFile.py | 16 ++ .../pyca/fernet/PycaMultiFernetTestFile.py | 19 ++ .../detection/pyca/hash/PycaHashDirectTest.py | 5 + .../pyca/kdf/PycaConcatKDFHMACTestFile.py | 15 ++ .../pyca/kdf/PycaConcatKDFHashTestFile.py | 13 + .../pyca/kdf/PycaHKDFExpandTestFile.py | 14 + .../detection/pyca/kdf/PycaHKDFTestFile.py | 15 ++ .../pyca/kdf/PycaKBKDFCMACTestFile.py | 21 ++ .../pyca/kdf/PycaKBKDFHMACTestFile.py | 22 ++ .../detection/pyca/kdf/PycaPBKDF2TestFile.py | 16 ++ .../detection/pyca/kdf/PycaScryptTestFile.py | 15 ++ .../detection/pyca/kdf/PycaX963KDFTestFile.py | 13 + .../keyagreement/PycaKeyAgreementTestFile.py | 38 +++ .../detection/pyca/mac/PycaCMACTestFile.py | 25 ++ .../detection/pyca/mac/PycaHMACTestFile.py | 25 ++ ...ycaMacDetectionInCustomFunctionTestFile.py | 27 ++ .../pyca/mac/PycaPoly1305TestFile.py | 21 ++ .../pyca/padding/PycaPaddingTestFile.py | 16 ++ .../pyca/symmetric/PycaCipher1TestFile.py | 21 ++ .../pyca/symmetric/PycaCipher2TestFile.py | 12 + .../pyca/symmetric/PycaCipher3TestFile.py | 36 +++ .../symmetric/PycaStreamCipher1TestFile.py | 11 + .../pyca/wrapping/PycaWrappingTestFile.py | 25 ++ .../PycaWrappingWithPaddingTestFile.py | 25 ++ .../detection/pyca/aead/PycaAESGCMTest.java | 148 ++++++++++ .../pyca/aead/PycaChaCha20Poly1305Test.java | 135 ++++++++++ .../asymmetric/DSA/PycaDSANumbersTest.java | 140 ++++++++++ .../pyca/asymmetric/DSA/PycaDSASignTest.java | 156 +++++++++++ .../PycaDiffieHellmanGenerateTest.java | 99 +++++++ .../PycaDiffieHellmanNumbersTest.java | 96 +++++++ .../PycaEllipticCurveDeriveTest.java | 103 +++++++ .../PycaEllipticCurveKeyExchangeTest.java | 206 ++++++++++++++ .../PycaEllipticCurveNumbersTest.java | 52 ++++ .../PycaEllipticCurveSign2Test.java | 185 +++++++++++++ .../PycaEllipticCurveSignTest.java | 158 +++++++++++ .../PycaEllipticCurveVerifyTest.java | 55 ++++ .../asymmetric/RSA/PycaRSADecryptTest.java | 253 ++++++++++++++++++ .../asymmetric/RSA/PycaRSANumbersTest.java | 142 ++++++++++ .../pyca/asymmetric/RSA/PycaRSASign1Test.java | 231 ++++++++++++++++ .../pyca/asymmetric/RSA/PycaRSASign2Test.java | 170 ++++++++++++ .../pyca/fernet/PycaFernetDecryptTest.java | 200 ++++++++++++++ .../pyca/fernet/PycaFernetEncryptTest.java | 200 ++++++++++++++ .../pyca/fernet/PycaMultiFernetTest.java | 211 +++++++++++++++ .../pyca/hash/PycaHashDirectTest.java | 102 +++++++ .../pyca/kdf/PycaConcatKDFHMACTest.java | 133 +++++++++ .../pyca/kdf/PycaConcatKDFHashTest.java | 131 +++++++++ .../pyca/kdf/PycaHKDFExpandTest.java | 133 +++++++++ .../detection/pyca/kdf/PycaHKDFTest.java | 133 +++++++++ .../detection/pyca/kdf/PycaKBKDFCMACTest.java | 142 ++++++++++ .../detection/pyca/kdf/PycaKBKDFHMACTest.java | 163 +++++++++++ .../detection/pyca/kdf/PycaPBKDF2Test.java | 147 ++++++++++ .../detection/pyca/kdf/PycaScryptTest.java | 101 +++++++ .../detection/pyca/kdf/PycaX963KDFTest.java | 133 +++++++++ .../keyagreement/PycaKeyAgreementTest.java | 179 +++++++++++++ .../detection/pyca/mac/PycaCMACTest.java | 101 +++++++ .../detection/pyca/mac/PycaHMACTest.java | 122 +++++++++ .../PycaMacDetectionInCustomFunctionTest.java | 131 +++++++++ .../detection/pyca/mac/PycaPoly1305Test.java | 80 ++++++ .../pyca/padding/PycaPaddingTest.java | 125 +++++++++ .../pyca/symmetric/PycaCipher1Test.java | 161 +++++++++++ .../pyca/symmetric/PycaCipher2Test.java | 104 +++++++ .../pyca/symmetric/PycaCipher3Test.java | 115 ++++++++ .../pyca/symmetric/PycaStreamCipher1Test.java | 89 ++++++ .../pyca/wrapping/PycaWrappingTest.java | 87 ++++++ .../wrapping/PycaWrappingWithPaddingTest.java | 88 ++++++ 82 files changed, 6431 insertions(+) create mode 100644 python/src/test/files/rules/detection/pyca/aead/PycaAESGCMTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/aead/PycaChaCha20Poly1305TestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/asymmetric/DSA/PycaDSANumbersTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/asymmetric/DSA/PycaDSASignTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSign2TestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSignTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSADecryptTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSANumbersTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSASign1TestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSASign2TestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/fernet/PycaFernetDecryptTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/fernet/PycaFernetEncryptTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/fernet/PycaMultiFernetTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/hash/PycaHashDirectTest.py create mode 100644 python/src/test/files/rules/detection/pyca/kdf/PycaConcatKDFHMACTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/kdf/PycaConcatKDFHashTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/kdf/PycaHKDFExpandTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/kdf/PycaHKDFTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/kdf/PycaKBKDFCMACTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/kdf/PycaKBKDFHMACTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/kdf/PycaPBKDF2TestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/kdf/PycaScryptTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/kdf/PycaX963KDFTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/keyagreement/PycaKeyAgreementTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/mac/PycaCMACTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/mac/PycaHMACTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/mac/PycaMacDetectionInCustomFunctionTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/mac/PycaPoly1305TestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/padding/PycaPaddingTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/symmetric/PycaCipher1TestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/symmetric/PycaCipher2TestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/symmetric/PycaCipher3TestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/symmetric/PycaStreamCipher1TestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/wrapping/PycaWrappingTestFile.py create mode 100644 python/src/test/files/rules/detection/pyca/wrapping/PycaWrappingWithPaddingTestFile.py create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/aead/PycaAESGCMTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/aead/PycaChaCha20Poly1305Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DSA/PycaDSANumbersTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DSA/PycaDSASignTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSign2Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSignTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSADecryptTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSANumbersTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSASign1Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSASign2Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaFernetDecryptTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaFernetEncryptTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaMultiFernetTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/hash/PycaHashDirectTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaConcatKDFHMACTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaConcatKDFHashTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaHKDFExpandTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaHKDFTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaKBKDFCMACTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaKBKDFHMACTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaPBKDF2Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaScryptTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaX963KDFTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/keyagreement/PycaKeyAgreementTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaCMACTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaHMACTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaMacDetectionInCustomFunctionTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaPoly1305Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/padding/PycaPaddingTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher1Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher2Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher3Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaStreamCipher1Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/wrapping/PycaWrappingTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pyca/wrapping/PycaWrappingWithPaddingTest.java diff --git a/python/src/test/files/rules/detection/pyca/aead/PycaAESGCMTestFile.py b/python/src/test/files/rules/detection/pyca/aead/PycaAESGCMTestFile.py new file mode 100644 index 000000000..84de0996e --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/aead/PycaAESGCMTestFile.py @@ -0,0 +1,10 @@ +import os +from cryptography.hazmat.primitives.ciphers.aead import AESGCM + +data = b"a secret message" +aad = b"authenticated but unencrypted data" +key = AESGCM.generate_key(bit_length=128) # Noncompliant {{(SecretKey) AES}} +aesgcm = AESGCM(key) +nonce = os.urandom(12) +ct = aesgcm.encrypt(nonce, data, aad) +aesgcm.decrypt(nonce, ct, aad) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/aead/PycaChaCha20Poly1305TestFile.py b/python/src/test/files/rules/detection/pyca/aead/PycaChaCha20Poly1305TestFile.py new file mode 100644 index 000000000..135304288 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/aead/PycaChaCha20Poly1305TestFile.py @@ -0,0 +1,11 @@ +import os +from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305 + +data = b"a secret message" +aad = b"authenticated but unencrypted data" +key = ChaCha20Poly1305.generate_key() # Noncompliant {{(SecretKey) ChaCha20}} +chacha = ChaCha20Poly1305(key) +nonce = os.urandom(12) +ct = chacha.encrypt(nonce, data, aad) +nonce2 = os.urandom(12) +chacha.decrypt(nonce2, ct, aad) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/asymmetric/DSA/PycaDSANumbersTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/DSA/PycaDSANumbersTestFile.py new file mode 100644 index 000000000..a2205d08d --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/asymmetric/DSA/PycaDSANumbersTestFile.py @@ -0,0 +1,13 @@ +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.asymmetric import dsa +from cryptography.hazmat.primitives.asymmetric.dsa import DSAPrivateNumbers + +def generate_dsa_key_from_parameters( + p, q, g, x, y +) -> dsa.DSAPrivateKey: + """ + Generates a DSA private key from parameters p, q, g, x, and y. + """ + public_numbers = dsa.DSAPublicNumbers(y, dsa.DSAParameterNumbers(p, q, g)) # Noncompliant {{(PublicKey) DSA}} + private_numbers = DSAPrivateNumbers(x, public_numbers) # Noncompliant {{(PrivateKey) DSA}} + return private_numbers.private_key(default_backend()) diff --git a/python/src/test/files/rules/detection/pyca/asymmetric/DSA/PycaDSASignTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/DSA/PycaDSASignTestFile.py new file mode 100644 index 000000000..4bed251fe --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/asymmetric/DSA/PycaDSASignTestFile.py @@ -0,0 +1,11 @@ +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import dsa + +private_key = dsa.generate_private_key( # Noncompliant {{(PrivateKey) DSA}} + key_size=1024, +) +data = b"this is some data I'd like to sign" +signature = private_key.sign( + data, + hashes.SHA256() +) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTestFile.py new file mode 100644 index 000000000..ea45be61b --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTestFile.py @@ -0,0 +1,6 @@ +from cryptography.hazmat.primitives.asymmetric import dh + +# Generate some parameters. These can be reused. +parameters = dh.generate_parameters(generator=2, key_size=2048) +# Generate a private key for use in the exchange. +server_private_key = parameters.generate_private_key() # Noncompliant {{(PrivateKey) FFDH}} diff --git a/python/src/test/files/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTestFile.py new file mode 100644 index 000000000..06a8ae83b --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTestFile.py @@ -0,0 +1,13 @@ +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.asymmetric import dh +from cryptography.hazmat.primitives.asymmetric.dh import DHPrivateNumbers + +def generate_dh_key_from_parameters( + p, g, x, y +) -> dh.DHPrivateKey: + """ + Generates a DH private key from parameters p, g, x, and y. + """ + public_numbers = dh.DHPublicNumbers(y, p, g) # Noncompliant {{(PublicKey) FFDH}} + private_numbers = DHPrivateNumbers(x, public_numbers) # Noncompliant {{(PublicKey) FFDH}} + return private_numbers.private_key(default_backend()) diff --git a/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTestFile.py new file mode 100644 index 000000000..e5709f211 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTestFile.py @@ -0,0 +1,12 @@ +# Code inspired by https://github.com/dimaqq/minioidc/blob/main/tests/test_minioidc.py + +import cryptography.hazmat.primitives.asymmetric.ec +import base64 + +TEST_PRIVATE_KEY = cryptography.hazmat.primitives.asymmetric.ec.derive_private_key( # Noncompliant {{(PrivateKey) EC-secp256r1}} + int.from_bytes( + base64.urlsafe_b64decode("870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE" + "==="), + "big", + ), + cryptography.hazmat.primitives.asymmetric.ec.SECP256R1(), +) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTestFile.py new file mode 100644 index 000000000..b1d727b32 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTestFile.py @@ -0,0 +1,21 @@ +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.hazmat.primitives.kdf.hkdf import HKDF + +# Generate a private key for use in the exchange. +server_private_key = ec.generate_private_key( # Noncompliant {{(PrivateKey) EC-secp384r1}} + ec.SECP384R1() +) + +def exchange(public_key): + shared_key = server_private_key.exchange( + ec.ECDH(), public_key) + + # Perform key derivation. // TODO: How should this key derivation be linked to the private key? + derived_key = HKDF( # Noncompliant {{(KeyDerivationFunction) HKDF-SHA-256}} + algorithm=hashes.SHA256(), + length=32, + salt=None, + info=b'handshake data', + ).derive(shared_key) + return derived_key \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTestFile.py new file mode 100644 index 000000000..38c65d80d --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTestFile.py @@ -0,0 +1,53 @@ +# Code inspired by https://github.com/ydb-platform/ydb/blob/284b7efb67edcdade0b12c849b7fad40739ad62b/contrib/python/Twisted/py2/twisted/conch/ssh/keys.py#L799 + +from cryptography.hazmat.primitives.asymmetric import dsa, rsa, padding, ec + +_curveTable = { + b'ecdsa-sha2-nistp256': ec.SECP256R1(), + b'ecdsa-sha2-nistp384': ec.SECP384R1(), + b'ecdsa-sha2-nistp521': ec.SECP521R1(), +} + +def default_backend(): + global _default_backend + + if _default_backend is None: + from cryptography.hazmat.backends.openssl.backend import backend + + _default_backend = backend + + return _default_backend + +class Key(object): + @classmethod + def _fromECComponents(cls, x, y, curve, privateValue=None): + """ + Build a key from EC components. + + @param x: The affine x component of the public point used for verifying. + @type x: L{int} + + @param y: The affine y component of the public point used for verifying. + @type y: L{int} + + @param curve: NIST name of elliptic curve. + @type curve: L{bytes} + + @param privateValue: The private value. + @type privateValue: L{int} + """ + + publicNumbers = ec.EllipticCurvePublicNumbers( + x=x, y=y, curve=_curveTable[curve]) + if privateValue is None: + # We have public components. + keyObject = publicNumbers.public_key(default_backend()) + else: + privateNumbers = ec.EllipticCurvePrivateNumbers( + private_value=privateValue, public_numbers=publicNumbers) + keyObject = privateNumbers.private_key(default_backend()) + + return cls(keyObject) + +some_var = b'ecdsa-sha2-nistp256' +Key._fromECComponents(None, None, None, some_var, None) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSign2TestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSign2TestFile.py new file mode 100644 index 000000000..56aab2093 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSign2TestFile.py @@ -0,0 +1,48 @@ +from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey +from cryptography.hazmat.primitives.asymmetric.ed448 import Ed448PrivateKey + +private_key = Ed25519PrivateKey.generate() # Noncompliant {{(PrivateKey) Ed25519}} +signature = private_key.sign(b"my authenticated message") +public_key = private_key.public_key() +# Raises InvalidSignature if verification fails +public_key.verify(signature, b"my authenticated message") + +private_key = Ed448PrivateKey.generate() # Noncompliant {{(PrivateKey) Ed448}} +signature = private_key.sign(b"my authenticated message") +public_key = private_key.public_key() +# Raises InvalidSignature if verification fails +public_key.verify(signature, b"my authenticated message") + +# False positives that should NOT be detected (PR-429 fix) +# These are unrelated generate() methods with parameters +class VLMModel: + def generate(self, **gen_kwargs): + return [1, 2, 3] + +class TextModel: + def generate(self, *prompts): + return "generated text" + +vlm_model = VLMModel() +text_model = TextModel() + +# These should NOT trigger detection (not cryptography-related) +generated_ids = vlm_model.generate(**{"max_length": 100}) +generated_text = text_model.generate("prompt1", "prompt2") + +# GROUND TRUTH (translation of the 1st finding) +# +# PrivateKey EC +# Signature EdDSA +# MessageDigest SHA-512 +# EllipticCurveAlgorithm EC +# EllipticCurve Curve25519 +# Sign SIGN +# EllipticCurveAlgorithm EC +# EllipticCurve Curve25519 +# KeyGeneration KEYGENERATION +# PublicKey EC +# EllipticCurveAlgorithm EC +# EllipticCurve Curve25519 +# KeyGeneration KEYGENERATION +# \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSignTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSignTestFile.py new file mode 100644 index 000000000..56fdeec3d --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSignTestFile.py @@ -0,0 +1,37 @@ +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.hazmat.primitives.asymmetric import utils + +# param = ec.SECP192R1() +param = ec.SECP384R1() +# private_key, other_var = ec.generate_private_key(param), 42 # TODO: because of TraceSeymbols not yet supporting multi-var assignments, this does not work +private_key = ec.generate_private_key(param) # Noncompliant {{(PrivateKey) EC-secp384r1}} + +# Ploys that should not be detected +b = ec.ECDSA(utils.Prehashed(hashes.SHA3_224())) # TODO: The test should pass also when removing "b =" +utils.Prehashed(hashes.SHA3_224()) +hashes.SHA3_224() + +digest = b"\x00" * 64 +sig = private_key.sign(digest, ec.ECDSA(utils.Prehashed(hashes.SHA3_512()))) + +# TODO: Make it work when uncommented +# pk = private_key.public_key() +# pk.verify(sig, digest, ec.ECDSA(hashes.SHA3_512())) + +# GROUND TRUTH (translation) +# +# PrivateKey EC +# Signature ECDSA +# MessageDigest SHA3-512 +# EllipticCurveAlgorithm EC +# EllipticCurve SECP384R1 +# Sign SIGN +# EllipticCurveAlgorithm EC +# EllipticCurve SECP384R1 +# KeyGeneration KEYGENERATION +# PublicKey EC +# EllipticCurveAlgorithm EC +# EllipticCurve SECP384R1 +# KeyGeneration KEYGENERATION +# diff --git a/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTestFile.py new file mode 100644 index 000000000..c357dea0f --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTestFile.py @@ -0,0 +1,8 @@ +# Code inspired by https://github.com/redis/redis-py/blob/master/redis/ocsp.py + +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import ec + +def verify(pubkey, signature, digest): + if isinstance(pubkey, ec.EllipticCurvePublicKey): + pubkey.verify(signature, digest, ec.ECDSA(hashes.SHA3_512())) diff --git a/python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSADecryptTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSADecryptTestFile.py new file mode 100644 index 000000000..a63b81590 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSADecryptTestFile.py @@ -0,0 +1,19 @@ +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import padding + +private_key = rsa.generate_private_key( # Noncompliant {{(PrivateKey) RSA}} + public_exponent=65537, + key_size=1024, +) + +def decrypt(ciphertext): + plaintext = private_key.decrypt( + ciphertext, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA384()), + algorithm=hashes.SHA256(), + label=None + ) + ) + return plaintext \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSANumbersTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSANumbersTestFile.py new file mode 100644 index 000000000..f84461215 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSANumbersTestFile.py @@ -0,0 +1,13 @@ +from cryptography.hazmat.primitives.asymmetric.rsa import * +from cryptography.hazmat.backends import default_backend + +def generate_rsa_key_from_parameters( + p, q, d, dmp1, dmq1, iqmp, e, n +) -> RSAPrivateKey: + """ + Note: from certbot dp is dmp1, dq is dmq1 and qi is iqmp + """ + public_numbers = RSAPublicNumbers(e, n) # Noncompliant {{(PublicKey) RSA}} + return RSAPrivateNumbers( # Noncompliant {{(PrivateKey) RSA}} + p, q, d, dmp1, dmq1, iqmp, public_numbers + ).private_key(default_backend()) diff --git a/python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSASign1TestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSASign1TestFile.py new file mode 100644 index 000000000..3d25ad2dd --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSASign1TestFile.py @@ -0,0 +1,19 @@ +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import padding +from cryptography.hazmat.primitives.asymmetric import utils + +private_key = rsa.generate_private_key( # Noncompliant {{(PrivateKey) RSA}} + public_exponent=65537, + key_size=2048, +) + +message = b"A message I want to sign" +signature = private_key.sign( + message, + padding.PSS( + mgf=padding.MGF1(hashes.SHA256()), + salt_length=padding.PSS.MAX_LENGTH + ), + utils.Prehashed(hashes.SHA384()) +) diff --git a/python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSASign2TestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSASign2TestFile.py new file mode 100644 index 000000000..43fd0ae39 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSASign2TestFile.py @@ -0,0 +1,16 @@ +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import padding +from cryptography.hazmat.primitives.asymmetric import utils + +private_key = rsa.generate_private_key( # Noncompliant {{(PrivateKey) RSA}} + public_exponent=65537, + key_size=2048, +) + +message = b"A message I want to sign" +signature = private_key.sign( + message, + padding.PKCS1v15(), + hashes.SHA3_384() +) diff --git a/python/src/test/files/rules/detection/pyca/fernet/PycaFernetDecryptTestFile.py b/python/src/test/files/rules/detection/pyca/fernet/PycaFernetDecryptTestFile.py new file mode 100644 index 000000000..0ba848d20 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/fernet/PycaFernetDecryptTestFile.py @@ -0,0 +1,15 @@ +from cryptography.fernet import Fernet + +def test1(): + key = Fernet.generate_key() # Noncompliant {{(SecretKey) Fernet}} + + def dec(ciphertext): + f = Fernet(key) + return f.decrypt(ciphertext) + +def test2(): + key = Fernet.generate_key() # Noncompliant {{(SecretKey) Fernet}} + + def dec(ciphertext, time): + f = Fernet(key) + return f.decrypt_at_time(ciphertext, time) diff --git a/python/src/test/files/rules/detection/pyca/fernet/PycaFernetEncryptTestFile.py b/python/src/test/files/rules/detection/pyca/fernet/PycaFernetEncryptTestFile.py new file mode 100644 index 000000000..e7b3eeb58 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/fernet/PycaFernetEncryptTestFile.py @@ -0,0 +1,16 @@ +from cryptography.fernet import Fernet + +def test1(): + key = Fernet.generate_key() # Noncompliant {{(SecretKey) Fernet}} + + def enc(data): + f = Fernet(key) + return f.encrypt(data) + +def test2(): + key = Fernet.generate_key() # Noncompliant {{(SecretKey) Fernet}} + + def enc(data, time): + f = Fernet(key) + return f.encrypt_at_time(data, time) + diff --git a/python/src/test/files/rules/detection/pyca/fernet/PycaMultiFernetTestFile.py b/python/src/test/files/rules/detection/pyca/fernet/PycaMultiFernetTestFile.py new file mode 100644 index 000000000..669b8f0d4 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/fernet/PycaMultiFernetTestFile.py @@ -0,0 +1,19 @@ +from cryptography.fernet import Fernet, MultiFernet + +key1 = Fernet(Fernet.generate_key()) # Noncompliant {{(SecretKey) Fernet}} +key2 = Fernet(Fernet.generate_key()) # Noncompliant {{(SecretKey) Fernet}} + +def enc(data): + return MultiFernet([key1, key2]).encrypt(data) + +def dec(data): + return MultiFernet([key1, key2]).decrypt(data) + +# TODO: When using the following code instead, the depending `encrypt` and `decrypt` are detected twice because the type resolution of `f` does not succeed (so it could be Fernet as well as MultiFernet) + +# f = MultiFernet([key1, key2]) +# def enc(data): +# return f.encrypt(data) + +# def dec(data): +# return f.decrypt(data) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/hash/PycaHashDirectTest.py b/python/src/test/files/rules/detection/pyca/hash/PycaHashDirectTest.py new file mode 100644 index 000000000..3b6afb9c1 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/hash/PycaHashDirectTest.py @@ -0,0 +1,5 @@ +from cryptography.hazmat.primitives import hashes + +sha256_obj = hashes.Hash(hashes.SHA256()) # Noncompliant {{(MessageDigest) SHA-256}} +sha256_obj.update(b"data") +digest = sha256_obj.finalize() diff --git a/python/src/test/files/rules/detection/pyca/kdf/PycaConcatKDFHMACTestFile.py b/python/src/test/files/rules/detection/pyca/kdf/PycaConcatKDFHMACTestFile.py new file mode 100644 index 000000000..bdd95889d --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/kdf/PycaConcatKDFHMACTestFile.py @@ -0,0 +1,15 @@ +import os +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.kdf.concatkdf import ConcatKDFHMAC + +salt = os.urandom(16) +otherinfo = b"concatkdf-example" + +ckdf = ConcatKDFHMAC( # Noncompliant {{(KeyDerivationFunction) ConcatenationKDF}} + algorithm=hashes.SHA256(), + length=32, + salt=salt, + otherinfo=otherinfo, +) + +key = ckdf.derive(b"input key") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/kdf/PycaConcatKDFHashTestFile.py b/python/src/test/files/rules/detection/pyca/kdf/PycaConcatKDFHashTestFile.py new file mode 100644 index 000000000..f4be827c0 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/kdf/PycaConcatKDFHashTestFile.py @@ -0,0 +1,13 @@ +import os +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.kdf.concatkdf import ConcatKDFHash + +otherinfo = b"concatkdf-example" + +ckdf = ConcatKDFHash( # Noncompliant {{(KeyDerivationFunction) ConcatenationKDF}} + algorithm=hashes.SHA256(), + length=64, + otherinfo=otherinfo, +) + +key = ckdf.derive(b"input key") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/kdf/PycaHKDFExpandTestFile.py b/python/src/test/files/rules/detection/pyca/kdf/PycaHKDFExpandTestFile.py new file mode 100644 index 000000000..081088184 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/kdf/PycaHKDFExpandTestFile.py @@ -0,0 +1,14 @@ +import os +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.kdf.hkdf import HKDFExpand + +info = b"hkdf-example" +key_material = os.urandom(16) + +hkdf = HKDFExpand( # Noncompliant {{(KeyDerivationFunction) HKDF-SHA-256}} + algorithm=hashes.SHA256(), + length=32, + info=info, +) + +key = hkdf.derive(key_material) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/kdf/PycaHKDFTestFile.py b/python/src/test/files/rules/detection/pyca/kdf/PycaHKDFTestFile.py new file mode 100644 index 000000000..eb0a673b0 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/kdf/PycaHKDFTestFile.py @@ -0,0 +1,15 @@ +import os +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.kdf.hkdf import HKDF + +salt = os.urandom(16) +info = b"hkdf-example" + +hkdf = HKDF( # Noncompliant {{(KeyDerivationFunction) HKDF-SHA-256}} + algorithm=hashes.SHA256(), + length=32, + salt=salt, + info=info, +) + +key = hkdf.derive(b"input key") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/kdf/PycaKBKDFCMACTestFile.py b/python/src/test/files/rules/detection/pyca/kdf/PycaKBKDFCMACTestFile.py new file mode 100644 index 000000000..abd8f7afb --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/kdf/PycaKBKDFCMACTestFile.py @@ -0,0 +1,21 @@ +from cryptography.hazmat.primitives.ciphers import algorithms +from cryptography.hazmat.primitives.kdf.kbkdf import ( + CounterLocation, KBKDFCMAC, Mode +) + +label = b"KBKDF CMAC Label" +context = b"KBKDF CMAC Context" + +kdf = KBKDFCMAC( # Noncompliant {{(Mac) CMAC-AES}} + algorithm=algorithms.AES, + mode=Mode.CounterMode, + length=32, + rlen=4, + llen=4, + location=CounterLocation.BeforeFixed, + label=label, + context=context, + fixed=None, +) + +key = kdf.derive(b"32 bytes long input key material") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/kdf/PycaKBKDFHMACTestFile.py b/python/src/test/files/rules/detection/pyca/kdf/PycaKBKDFHMACTestFile.py new file mode 100644 index 000000000..f29af21d6 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/kdf/PycaKBKDFHMACTestFile.py @@ -0,0 +1,22 @@ +import os +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.kdf.kbkdf import ( + CounterLocation, KBKDFHMAC, Mode +) + +label = b"KBKDF HMAC Label" +context = b"KBKDF HMAC Context" + +kdf = KBKDFHMAC( # Noncompliant {{(Mac) HMAC-SHA-256}} + algorithm=hashes.SHA256(), + mode=Mode.CounterMode, + length=32, + rlen=4, + llen=4, + location=CounterLocation.BeforeFixed, + label=label, + context=context, + fixed=None, +) + +key = kdf.derive(b"input key") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/kdf/PycaPBKDF2TestFile.py b/python/src/test/files/rules/detection/pyca/kdf/PycaPBKDF2TestFile.py new file mode 100644 index 000000000..6baeb886c --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/kdf/PycaPBKDF2TestFile.py @@ -0,0 +1,16 @@ +import os +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC + +# Salts should be randomly generated +salt = os.urandom(16) + +# derive +kdf = PBKDF2HMAC( # Noncompliant {{(PasswordBasedKeyDerivationFunction) PBKDF2-SHA-256}} + algorithm=hashes.SHA256(), + length=32, + salt=salt, + iterations=480000, +) + +key = kdf.derive(b"my great password") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/kdf/PycaScryptTestFile.py b/python/src/test/files/rules/detection/pyca/kdf/PycaScryptTestFile.py new file mode 100644 index 000000000..42c5ad086 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/kdf/PycaScryptTestFile.py @@ -0,0 +1,15 @@ +import os +from cryptography.hazmat.primitives.kdf.scrypt import Scrypt + +salt = os.urandom(16) + +# derive +kdf = Scrypt( # Noncompliant {{(PasswordBasedKeyDerivationFunction) scrypt}} + salt=salt, + length=32, + n=2**14, + r=8, + p=1, +) + +key = kdf.derive(b"my great password") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/kdf/PycaX963KDFTestFile.py b/python/src/test/files/rules/detection/pyca/kdf/PycaX963KDFTestFile.py new file mode 100644 index 000000000..10c0f51c7 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/kdf/PycaX963KDFTestFile.py @@ -0,0 +1,13 @@ +import os +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.kdf.x963kdf import X963KDF + +sharedinfo = b"ANSI-KDF-X9.63 Example" + +xkdf = X963KDF( # Noncompliant {{(KeyDerivationFunction) ANSI-KDF-X9.63}} + algorithm=hashes.SHA256(), + length=32, + sharedinfo=sharedinfo, +) + +key = xkdf.derive(b"input key") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/keyagreement/PycaKeyAgreementTestFile.py b/python/src/test/files/rules/detection/pyca/keyagreement/PycaKeyAgreementTestFile.py new file mode 100644 index 000000000..f3212f37a --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/keyagreement/PycaKeyAgreementTestFile.py @@ -0,0 +1,38 @@ +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey +from cryptography.hazmat.primitives.asymmetric.x448 import X448PrivateKey + +# Generate a private key for use in the exchange. +private_key = X25519PrivateKey.generate() # Noncompliant {{(KeyAgreement) x25519}} +# In a real handshake the peer_public_key will be received from the +# other party. For this example we'll generate another private key and +# get a public key from that. Note that in a DH handshake both peers +# must agree on a common set of parameters. +peer_public_key = X25519PrivateKey.generate().public_key() # Noncompliant {{(KeyAgreement) x25519}} +shared_key = private_key.exchange(peer_public_key) + +# Generate a private key for use in the exchange. +private_key = X448PrivateKey.generate() # Noncompliant {{(KeyAgreement) x448}} +# In a real handshake the peer_public_key will be received from the +# other party. For this example we'll generate another private key and +# get a public key from that. Note that in a DH handshake both peers +# must agree on a common set of parameters. +peer_public_key = X448PrivateKey.generate().public_key() # Noncompliant {{(KeyAgreement) x448}} +shared_key = private_key.exchange(peer_public_key) + +# False positives that should NOT be detected (PR-429 fix) +# These are unrelated generate() methods with parameters +class DataGenerator: + def generate(self, **kwargs): + return [1, 2, 3] + +class ModelGenerator: + def generate(self, *args): + return "generated" + +data_gen = DataGenerator() +model_gen = ModelGenerator() + +# These should NOT trigger detection (not cryptography-related) +result1 = data_gen.generate(**{"size": 100}) +result2 = model_gen.generate("prompt1", "prompt2") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/mac/PycaCMACTestFile.py b/python/src/test/files/rules/detection/pyca/mac/PycaCMACTestFile.py new file mode 100644 index 000000000..1d86fa264 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/mac/PycaCMACTestFile.py @@ -0,0 +1,25 @@ +from cryptography.hazmat.primitives import cmac +from cryptography.hazmat.primitives.ciphers import algorithms + +def generate_cmac(key, data): + # Selecting the desired algorithm (e.g., CMAC-AES) + algorithm = algorithms.AES(key) + + # Creating the CMAC context + cmac_ctx = cmac.CMAC(algorithm) # Noncompliant {{(Mac) CMAC-AES}} + + # Updating the context with the data + cmac_ctx.update(data) + + # Finalizing the CMAC computation and getting the CMAC value + cmac_value = cmac_ctx.finalize() + + return cmac_value + +# Example usage +if __name__ == "__main__": + key = b'Sixteen byte key' # 16-byte key for AES + data = b'This is some data' # Data to generate CMAC for + + cmac_value = generate_cmac(key, data) + print("Generated CMAC:", cmac_value.hex()) diff --git a/python/src/test/files/rules/detection/pyca/mac/PycaHMACTestFile.py b/python/src/test/files/rules/detection/pyca/mac/PycaHMACTestFile.py new file mode 100644 index 000000000..70490920a --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/mac/PycaHMACTestFile.py @@ -0,0 +1,25 @@ +from cryptography.hazmat.primitives import hmac +from cryptography.hazmat.primitives import hashes + +def generate_hmac(key, data): + # Selecting the desired hash algorithm (e.g., SHA-256) + algorithm = hashes.SHA256() + + # Creating the HMAC context + hmac_ctx = hmac.HMAC(key, algorithm) # Noncompliant {{(Mac) HMAC-SHA-256}} + + # Updating the context with the data + hmac_ctx.update(data) + + # Finalizing the HMAC computation and getting the HMAC value + hmac_value = hmac_ctx.finalize() + + return hmac_value + +# Example usage +if __name__ == "__main__": + key = b'SecretKey123' # Key for HMAC + data = b'This is some data' # Data to generate HMAC for + + hmac_value = generate_hmac(key, data) + print("Generated HMAC:", hmac_value.hex()) diff --git a/python/src/test/files/rules/detection/pyca/mac/PycaMacDetectionInCustomFunctionTestFile.py b/python/src/test/files/rules/detection/pyca/mac/PycaMacDetectionInCustomFunctionTestFile.py new file mode 100644 index 000000000..ec0a20fc1 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/mac/PycaMacDetectionInCustomFunctionTestFile.py @@ -0,0 +1,27 @@ +from cryptography.hazmat.primitives import hmac +from cryptography.hazmat.primitives import hashes + +def custom_sign(key, data): + # Custom function with cryptographic operation + algorithm = hashes.SHA256() + hmac_obj = hmac.HMAC(key, algorithm) # Noncompliant {{(Mac) HMAC-SHA-256}} + hmac_obj.update(data) + return hmac_obj.finalize() + +def non_crypto_function(text): + # Non-cryptographic function - should not trigger detection + result = "not crypto: " + text + return result.upper() + +# Example usage +if __name__ == "__main__": + key = b'SecretKey123' + data = b'This is some data' + + # Cryptographic operation in custom function is detected + result = custom_sign(key, data) + print("HMAC Result:", result.hex()) + + # Non-cryptographic function call does not trigger detection + text_result = non_crypto_function("hello") + print("Text Result:", text_result) diff --git a/python/src/test/files/rules/detection/pyca/mac/PycaPoly1305TestFile.py b/python/src/test/files/rules/detection/pyca/mac/PycaPoly1305TestFile.py new file mode 100644 index 000000000..2fbaec802 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/mac/PycaPoly1305TestFile.py @@ -0,0 +1,21 @@ +from cryptography.hazmat.primitives.poly1305 import Poly1305 + +def generate_poly1305(key, data): + # Create a Poly1305 context with the given key + poly1305_ctx = Poly1305(key) # Noncompliant {{(Mac) Poly1305}} + + # Update the context with the data + poly1305_ctx.update(data) + + # Finalize the Poly1305 computation and get the authentication tag + poly1305_tag = poly1305_ctx.finalize() + + return poly1305_tag + +# Example usage +if __name__ == "__main__": + key = b'Sixteen byte key' # 16-byte key for Poly1305 + data = b'This is some data' # Data to generate Poly1305 tag for + + poly1305_tag = generate_poly1305(key, data) + print("Generated Poly1305 Tag:", poly1305_tag.hex()) diff --git a/python/src/test/files/rules/detection/pyca/padding/PycaPaddingTestFile.py b/python/src/test/files/rules/detection/pyca/padding/PycaPaddingTestFile.py new file mode 100644 index 000000000..7485f8b52 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/padding/PycaPaddingTestFile.py @@ -0,0 +1,16 @@ +import os +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.hazmat.primitives import padding + +key = os.urandom(32) +iv = os.urandom(16) +# Create a cipher object +cipher = Cipher(algorithms.CAST5(key), modes.CFB(iv)) # Noncompliant {{(BlockCipher) CAST5-CFB}} + +padder = padding.ANSIX923(128).padder() +padded_data = padder.update(b"a secret message") +print(padded_data) +padded_data += padder.finalize() +print(padded_data) + +# Then, one could use the cipher to encrypt the padded data \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/symmetric/PycaCipher1TestFile.py b/python/src/test/files/rules/detection/pyca/symmetric/PycaCipher1TestFile.py new file mode 100644 index 000000000..b95d70dac --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/symmetric/PycaCipher1TestFile.py @@ -0,0 +1,21 @@ +import os +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.hazmat.primitives.padding import PKCS7 + +key = os.urandom(32) +iv = os.urandom(16) +# Create a cipher object +cipher = Cipher(algorithms.AES(key), modes.CBC(iv)) # Noncompliant {{(BlockCipher) AES-CBC-PKCS7}} + +# Specify padding (PKCS7 in this case) +padder = PKCS7(algorithms.AES.block_size).padder() + +# Encrypt +encryptor = cipher.encryptor() +padded_data = padder.update(b"a secret message") + padder.finalize() +ct = encryptor.update(padded_data) + encryptor.finalize() + +# Decrypt +decryptor = cipher.decryptor() +padded_res = decryptor.update(ct) + decryptor.finalize() +unpadded_res = padder.update(padded_res) + padder.finalize() \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/symmetric/PycaCipher2TestFile.py b/python/src/test/files/rules/detection/pyca/symmetric/PycaCipher2TestFile.py new file mode 100644 index 000000000..3372719a6 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/symmetric/PycaCipher2TestFile.py @@ -0,0 +1,12 @@ +import os +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.hazmat.primitives.padding import PKCS7 + +key = os.urandom(32) +iv = os.urandom(16) +# Create a cipher object +cipher = Cipher(algorithms.Camellia(key), modes.OFB(iv)) # Noncompliant {{(BlockCipher) CAMELLIA-OFB}} + +# Encrypt +encryptor = cipher.encryptor() +ct = encryptor.update(b"a secret message") + encryptor.finalize() diff --git a/python/src/test/files/rules/detection/pyca/symmetric/PycaCipher3TestFile.py b/python/src/test/files/rules/detection/pyca/symmetric/PycaCipher3TestFile.py new file mode 100644 index 000000000..45ca10c8c --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/symmetric/PycaCipher3TestFile.py @@ -0,0 +1,36 @@ + +import os +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes + +key64 = b"01234567" +key128 = b"0123456789abcdef" +key192 = b"0123456789abcdef01234567" +key256 = b"0123456789abcdef0123456789abcdef" + +iv = b"1234567890abcdef" +nonce = b"123456789012" +data = b"hello world!!!!!" + +# AES-CBC (invalid 64-bit key — should fail at runtime, but useful for static detection testing) +algo_small = algorithms.AES(key64) +c_small = Cipher(algo_small, modes.CBC(iv)) # Noncompliant {{(BlockCipher) AES-CBC}} +encryptor_small = c_small.encryptor() +ct_small = encryptor_small.update(data) + encryptor_small.finalize() + +# AES-CBC (128-bit) +algo_128 = algorithms.AES(key128) +c_128 = Cipher(algo_128, modes.CBC(iv)) # Noncompliant {{(BlockCipher) AES-CBC}} +encryptor_128 = c_128.encryptor() +ct_128 = encryptor_128.update(data) + encryptor_128.finalize() + +# AES-CBC (192-bit) +algo_192 = algorithms.AES(key192) +c_192 = Cipher(algo_192, modes.CBC(iv)) # Noncompliant {{(BlockCipher) AES-CBC}} +encryptor_192 = c_192.encryptor() +ct_192 = encryptor_192.update(data) + encryptor_192.finalize() + +# AES-CBC (256-bit) +algo_256 = algorithms.AES(key256) +c_256 = Cipher(algo_256, modes.CBC(iv)) # Noncompliant {{(BlockCipher) AES-CBC}} +encryptor_256 = c_256.encryptor() +ct_256 = encryptor_256.update(data) + encryptor_256.finalize() \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pyca/symmetric/PycaStreamCipher1TestFile.py b/python/src/test/files/rules/detection/pyca/symmetric/PycaStreamCipher1TestFile.py new file mode 100644 index 000000000..a1ff78550 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/symmetric/PycaStreamCipher1TestFile.py @@ -0,0 +1,11 @@ +import os +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes + +key = os.urandom(32) +iv = os.urandom(16) +# Create a cipher object +cipher = Cipher(algorithms.ChaCha20(key, nonce), mode=None) # Noncompliant {{(StreamCipher) ChaCha20}} + +# Encrypt +encryptor = cipher.encryptor() +ct = encryptor.update(b"a secret message") + encryptor.finalize() diff --git a/python/src/test/files/rules/detection/pyca/wrapping/PycaWrappingTestFile.py b/python/src/test/files/rules/detection/pyca/wrapping/PycaWrappingTestFile.py new file mode 100644 index 000000000..f4b3093e4 --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/wrapping/PycaWrappingTestFile.py @@ -0,0 +1,25 @@ +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.keywrap import aes_key_wrap, aes_key_unwrap + +def aes_key_wrap_example(): + # Generate a key to wrap + key_to_wrap = b'Sixteen byte key' + + # Generate wrapping key (must be 128, 192, or 256 bits long) + wrapping_key = b'ABCDEFGHIJKLMNOP' + + # Wrap the key + wrapped_key = aes_key_wrap(wrapping_key, key_to_wrap, default_backend()) # Noncompliant {{(KeyWrap) AES-128}} + + print("Wrapped Key:", wrapped_key.hex()) + + # Unwrap the key + unwrapped_key = aes_key_unwrap(wrapping_key, wrapped_key, default_backend()) + + print("Unwrapped Key:", unwrapped_key.hex()) + + # Ensure that the unwrapped key matches the original key + assert unwrapped_key == key_to_wrap + +if __name__ == "__main__": + aes_key_wrap_example() diff --git a/python/src/test/files/rules/detection/pyca/wrapping/PycaWrappingWithPaddingTestFile.py b/python/src/test/files/rules/detection/pyca/wrapping/PycaWrappingWithPaddingTestFile.py new file mode 100644 index 000000000..62580615e --- /dev/null +++ b/python/src/test/files/rules/detection/pyca/wrapping/PycaWrappingWithPaddingTestFile.py @@ -0,0 +1,25 @@ +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.keywrap import aes_key_wrap_with_padding, aes_key_unwrap_with_padding + +def aes_key_wrap_example(): + # Generate a key to wrap + key_to_wrap = b'Sixteen byte key' + + # Generate wrapping key (must be 128, 192, or 256 bits long) + wrapping_key = b'ABCDEFGHIJKLMNOP' + + # Wrap the key + wrapped_key = aes_key_wrap_with_padding(wrapping_key, key_to_wrap, default_backend()) # Noncompliant {{(KeyWrap) AES-128}} + + print("Wrapped Key:", wrapped_key.hex()) + + # Unwrap the key + unwrapped_key = aes_key_unwrap_with_padding(wrapping_key, wrapped_key, default_backend()) + + print("Unwrapped Key:", unwrapped_key.hex()) + + # Ensure that the unwrapped key matches the original key + assert unwrapped_key == key_to_wrap + +if __name__ == "__main__": + aes_key_wrap_example() diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/aead/PycaAESGCMTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/aead/PycaAESGCMTest.java new file mode 100644 index 000000000..7811c3317 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/aead/PycaAESGCMTest.java @@ -0,0 +1,148 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.aead; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.context.SecretKeyContext; +import com.ibm.mapper.model.AuthenticatedEncryption; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.Mode; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.SecretKey; +import com.ibm.mapper.model.functionality.Decrypt; +import com.ibm.mapper.model.functionality.Encrypt; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaAESGCMTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/aead/PycaAESGCMTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(SecretKeyContext.class); + assertThat(value).isInstanceOf(KeySize.class); + assertThat(value.asString()).isEqualTo("128"); + + assertThat(detectionStore.getChildren()).hasSize(2); + + DetectionStore store = + detectionStore.getChildren().get(0); + IValue decryptValue = store.getDetectionValues().get(0); + assertThat(store.getDetectionValueContext()).isInstanceOf(CipherContext.class); + assertThat(decryptValue).isInstanceOf(CipherAction.class); + assertThat(decryptValue.asString()).isEqualTo("DECRYPT"); + + store = detectionStore.getChildren().get(1); + IValue encryptValue = store.getDetectionValues().get(0); + assertThat(store.getDetectionValueContext()).isInstanceOf(CipherContext.class); + assertThat(encryptValue).isInstanceOf(CipherAction.class); + assertThat(encryptValue.asString()).isEqualTo("ENCRYPT"); + + /* + * Translation + */ + + assertThat(nodes).hasSize(1); + + // SecretKey + INode secretKeyNode = nodes.get(0); + assertThat(secretKeyNode.getKind()).isEqualTo(SecretKey.class); + assertThat(secretKeyNode.getChildren()).hasSize(4); + assertThat(secretKeyNode.asString()).isEqualTo("AES"); + + // AuthenticatedEncryption under SecretKey + INode authenticatedEncryptionNode = + secretKeyNode.getChildren().get(AuthenticatedEncryption.class); + assertThat(authenticatedEncryptionNode).isNotNull(); + assertThat(authenticatedEncryptionNode.getChildren()).hasSize(4); + assertThat(authenticatedEncryptionNode.asString()).isEqualTo("AES-128-GCM"); + + // Mode under AuthenticatedEncryption under SecretKey + INode modeNode = authenticatedEncryptionNode.getChildren().get(Mode.class); + assertThat(modeNode).isNotNull(); + assertThat(modeNode.getChildren()).isEmpty(); + assertThat(modeNode.asString()).isEqualTo("GCM"); + + // BlockSize under AuthenticatedEncryption under SecretKey + INode blockSizeNode = authenticatedEncryptionNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("128"); + + // Oid under AuthenticatedEncryption under SecretKey + INode oidNode = authenticatedEncryptionNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.1.6"); + + // KeyLength under AuthenticatedEncryption under SecretKey + INode keyLengthNode = authenticatedEncryptionNode.getChildren().get(KeyLength.class); + assertThat(keyLengthNode).isNotNull(); + assertThat(keyLengthNode.getChildren()).isEmpty(); + assertThat(keyLengthNode.asString()).isEqualTo("128"); + + // Encrypt under SecretKey + INode encryptNode = secretKeyNode.getChildren().get(Encrypt.class); + assertThat(encryptNode).isNotNull(); + assertThat(encryptNode.getChildren()).isEmpty(); + assertThat(encryptNode.asString()).isEqualTo("ENCRYPT"); + + // Decrypt under SecretKey + INode decryptNode = secretKeyNode.getChildren().get(Decrypt.class); + assertThat(decryptNode).isNotNull(); + assertThat(decryptNode.getChildren()).isEmpty(); + assertThat(decryptNode.asString()).isEqualTo("DECRYPT"); + + // Generate under SecretKey + INode generateNode = secretKeyNode.getChildren().get(KeyGeneration.class); + assertThat(generateNode).isNotNull(); + assertThat(generateNode.getChildren()).isEmpty(); + assertThat(generateNode.asString()).isEqualTo("KEYGENERATION"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/aead/PycaChaCha20Poly1305Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/aead/PycaChaCha20Poly1305Test.java new file mode 100644 index 000000000..88c1b01f4 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/aead/PycaChaCha20Poly1305Test.java @@ -0,0 +1,135 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.aead; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.context.SecretKeyContext; +import com.ibm.mapper.model.AuthenticatedEncryption; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.SecretKey; +import com.ibm.mapper.model.functionality.Decrypt; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.Encrypt; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaChaCha20Poly1305Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/aead/PycaChaCha20Poly1305TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(SecretKeyContext.class); + assertThat(value).isInstanceOf(KeyAction.class); + assertThat(value.asString()).isEqualTo("GENERATION"); + + assertThat(detectionStore.getChildren()).hasSize(2); + + DetectionStore store = + detectionStore.getChildren().get(0); + IValue decryptValue = store.getDetectionValues().get(0); + assertThat(store.getDetectionValueContext()).isInstanceOf(CipherContext.class); + assertThat(decryptValue).isInstanceOf(CipherAction.class); + assertThat(decryptValue.asString()).isEqualTo("ENCRYPT"); + + store = detectionStore.getChildren().get(1); + IValue encryptValue = store.getDetectionValues().get(0); + assertThat(store.getDetectionValueContext()).isInstanceOf(CipherContext.class); + assertThat(encryptValue).isInstanceOf(CipherAction.class); + assertThat(encryptValue.asString()).isEqualTo("DECRYPT"); + + /* + * Translation + */ + + assertThat(nodes).hasSize(1); + + // SecretKey + INode secretKeyNode = nodes.get(0); + assertThat(secretKeyNode.getKind()).isEqualTo(SecretKey.class); + assertThat(secretKeyNode.getChildren()).hasSize(4); + assertThat(secretKeyNode.asString()).isEqualTo("ChaCha20"); + + // Encrypt under SecretKey + INode encryptNode = secretKeyNode.getChildren().get(Encrypt.class); + assertThat(encryptNode).isNotNull(); + assertThat(encryptNode.getChildren()).isEmpty(); + assertThat(encryptNode.asString()).isEqualTo("ENCRYPT"); + + // Decrypt under SecretKey + INode decryptNode = secretKeyNode.getChildren().get(Decrypt.class); + assertThat(decryptNode).isNotNull(); + assertThat(decryptNode.getChildren()).isEmpty(); + assertThat(decryptNode.asString()).isEqualTo("DECRYPT"); + + // KeyGeneration under SecretKey + INode keyGenerationNode = secretKeyNode.getChildren().get(KeyGeneration.class); + assertThat(keyGenerationNode).isNotNull(); + assertThat(keyGenerationNode.getChildren()).isEmpty(); + assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); + + // AuthenticatedEncryption under SecretKey + INode authenticatedEncryptionNode = + secretKeyNode.getChildren().get(AuthenticatedEncryption.class); + assertThat(authenticatedEncryptionNode).isNotNull(); + assertThat(authenticatedEncryptionNode.getChildren()).hasSize(1); + assertThat(authenticatedEncryptionNode.asString()).isEqualTo("ChaCha20-Poly1305"); + + // MessageDigest under AuthenticatedEncryption under SecretKey + INode messageDigestNode = + authenticatedEncryptionNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode).isNotNull(); + assertThat(messageDigestNode.getChildren()).hasSize(1); + assertThat(messageDigestNode.asString()).isEqualTo("Poly1305"); + + // Digest under MessageDigest under AuthenticatedEncryption under SecretKey + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DSA/PycaDSANumbersTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DSA/PycaDSANumbersTest.java new file mode 100644 index 000000000..beebbd7c6 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DSA/PycaDSANumbersTest.java @@ -0,0 +1,140 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.asymmetric.DSA; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.model.context.PublicKeyContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.PublicKey; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaDSANumbersTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/asymmetric/DSA/PycaDSANumbersTestFile.py", + this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + + if (findingId == 0) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(PublicKeyContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(KeyAction.class); + assertThat(value0.asString()).isEqualTo("GENERATION"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // PublicKey + INode publicKeyNode = nodes.get(0); + assertThat(publicKeyNode.getKind()).isEqualTo(PublicKey.class); + assertThat(publicKeyNode.getChildren()).hasSize(2); + assertThat(publicKeyNode.asString()).isEqualTo("DSA"); + + // Signature under PublicKey + INode signatureNode = publicKeyNode.getChildren().get(Signature.class); + assertThat(signatureNode).isNotNull(); + assertThat(signatureNode.getChildren()).hasSize(1); + assertThat(signatureNode.asString()).isEqualTo("DSA"); + + // Oid under Signature under PublicKey + INode oidNode = signatureNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("1.2.840.10040.4.1"); + + // KeyGeneration under PublicKey + INode keyGenerationNode = publicKeyNode.getChildren().get(KeyGeneration.class); + assertThat(keyGenerationNode).isNotNull(); + assertThat(keyGenerationNode.getChildren()).isEmpty(); + assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); + } else { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(PrivateKeyContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(KeyAction.class); + assertThat(value0.asString()).isEqualTo("GENERATION"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // PrivateKey + INode privateKeyNode = nodes.get(0); + assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); + assertThat(privateKeyNode.getChildren()).hasSize(2); + assertThat(privateKeyNode.asString()).isEqualTo("DSA"); + + // Signature under PrivateKey + INode signatureNode = privateKeyNode.getChildren().get(Signature.class); + assertThat(signatureNode).isNotNull(); + assertThat(signatureNode.getChildren()).hasSize(1); + assertThat(signatureNode.asString()).isEqualTo("DSA"); + + // Oid under Signature under PrivateKey + INode oidNode = signatureNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("1.2.840.10040.4.1"); + + // KeyGeneration under PrivateKey + INode keyGenerationNode = privateKeyNode.getChildren().get(KeyGeneration.class); + assertThat(keyGenerationNode).isNotNull(); + assertThat(keyGenerationNode.getChildren()).isEmpty(); + assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DSA/PycaDSASignTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DSA/PycaDSASignTest.java new file mode 100644 index 000000000..1c4cfcafe --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DSA/PycaDSASignTest.java @@ -0,0 +1,156 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.asymmetric.DSA; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Sign; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaDSASignTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/asymmetric/DSA/PycaDSASignTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(PrivateKeyContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(KeySize.class); + assertThat(value0.asString()).isEqualTo("1024"); + + DetectionStore store_1 = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(store_1.getDetectionValues()).hasSize(1); + assertThat(store_1.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue value0_1 = store_1.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(SignatureAction.class); + assertThat(value0_1.asString()).isEqualTo("SIGN"); + + DetectionStore store_1_1 = + getStoreOfValueType(ValueAction.class, store_1.getChildren()); + assertThat(store_1_1.getDetectionValues()).hasSize(1); + assertThat(store_1_1.getDetectionValueContext()).isInstanceOf(DigestContext.class); + IValue value0_1_1 = store_1_1.getDetectionValues().get(0); + assertThat(value0_1_1).isInstanceOf(ValueAction.class); + assertThat(value0_1_1.asString()).isEqualTo("SHA256"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // PrivateKey + INode privateKeyNode = nodes.get(0); + assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); + assertThat(privateKeyNode.getChildren()).hasSize(4); + assertThat(privateKeyNode.asString()).isEqualTo("DSA"); + + // Signature under PrivateKey + INode signatureNode = privateKeyNode.getChildren().get(Signature.class); + assertThat(signatureNode).isNotNull(); + assertThat(signatureNode.getChildren()).hasSize(2); + assertThat(signatureNode.asString()).isEqualTo("DSA-SHA-256"); + + // Oid under Signature under PrivateKey + INode oidNode = signatureNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.3.2"); + + // MessageDigest under Signature under PrivateKey + INode messageDigestNode = signatureNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode).isNotNull(); + assertThat(messageDigestNode.getChildren()).hasSize(4); + assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); + + // BlockSize under MessageDigest under Signature under PrivateKey + INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("512"); + + // Oid under MessageDigest under Signature under PrivateKey + INode oidNode1 = messageDigestNode.getChildren().get(Oid.class); + assertThat(oidNode1).isNotNull(); + assertThat(oidNode1.getChildren()).isEmpty(); + assertThat(oidNode1.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + + // DigestSize under MessageDigest under Signature under PrivateKey + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("256"); + + // Digest under MessageDigest under Signature under PrivateKey + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // KeyGeneration under PrivateKey + INode keyGenerationNode = privateKeyNode.getChildren().get(KeyGeneration.class); + assertThat(keyGenerationNode).isNotNull(); + assertThat(keyGenerationNode.getChildren()).isEmpty(); + assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); + + // Sign under PrivateKey + INode signNode = privateKeyNode.getChildren().get(Sign.class); + assertThat(signNode).isNotNull(); + assertThat(signNode.getChildren()).isEmpty(); + assertThat(signNode.asString()).isEqualTo("SIGN"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTest.java new file mode 100644 index 000000000..4097dcf75 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTest.java @@ -0,0 +1,99 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.asymmetric.DiffieHellman; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public final class PycaDiffieHellmanGenerateTest extends TestBase { + + // The key size does not yet appear because + // of the TraceSymbol problem documented on the Github issue + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTestFile.py", + this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(PrivateKeyContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(KeyAction.class); + assertThat(value0.asString()).isEqualTo("GENERATION"); + + /* + * Translation + */ + + assertThat(nodes).hasSize(1); + + // PrivateKey + INode privateKeyNode = nodes.get(0); + assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); + assertThat(privateKeyNode.getChildren()).hasSize(2); + assertThat(privateKeyNode.asString()).isEqualTo("FFDH"); + + // KeyGeneration under PrivateKey + INode keyGenerationNode = privateKeyNode.getChildren().get(KeyGeneration.class); + assertThat(keyGenerationNode).isNotNull(); + assertThat(keyGenerationNode.getChildren()).isEmpty(); + assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); + + // PublicKeyEncryption under PrivateKey + INode publicKeyEncryptionNode = privateKeyNode.getChildren().get(PublicKeyEncryption.class); + assertThat(publicKeyEncryptionNode).isNotNull(); + assertThat(publicKeyEncryptionNode.getChildren()).hasSize(1); + assertThat(publicKeyEncryptionNode.asString()).isEqualTo("FFDH"); + + // Oid under PublicKeyEncryption under PrivateKey + INode oidNode = publicKeyEncryptionNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("1.2.840.113549.1.3.1"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTest.java new file mode 100644 index 000000000..26099a4a9 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTest.java @@ -0,0 +1,96 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.asymmetric.DiffieHellman; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.context.PublicKeyContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PublicKey; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaDiffieHellmanNumbersTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTestFile.py", + this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(PublicKeyContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(KeyAction.class); + assertThat(value0.asString()).isEqualTo("GENERATION"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // PublicKey + INode publicKeyNode1 = nodes.get(0); + assertThat(publicKeyNode1.getKind()).isEqualTo(PublicKey.class); + assertThat(publicKeyNode1.getChildren()).hasSize(2); + assertThat(publicKeyNode1.asString()).isEqualTo("FFDH"); + + // PublicKeyEncryption under PublicKey + INode publicKeyEncryptionNode1 = + publicKeyNode1.getChildren().get(PublicKeyEncryption.class); + assertThat(publicKeyEncryptionNode1).isNotNull(); + assertThat(publicKeyEncryptionNode1.getChildren()).hasSize(1); + assertThat(publicKeyEncryptionNode1.asString()).isEqualTo("FFDH"); + + // Oid under PublicKeyEncryption under PublicKey + INode oidNode1 = publicKeyEncryptionNode1.getChildren().get(Oid.class); + assertThat(oidNode1).isNotNull(); + assertThat(oidNode1.getChildren()).isEmpty(); + assertThat(oidNode1.asString()).isEqualTo("1.2.840.113549.1.3.1"); + + // KeyGeneration under PublicKey + INode keyGenerationNode1 = publicKeyNode1.getChildren().get(KeyGeneration.class); + assertThat(keyGenerationNode1).isNotNull(); + assertThat(keyGenerationNode1.getChildren()).isEmpty(); + assertThat(keyGenerationNode1.asString()).isEqualTo("KEYGENERATION"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTest.java new file mode 100644 index 000000000..d34d4fa91 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTest.java @@ -0,0 +1,103 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.asymmetric.EllipticCurve; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Curve; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.mapper.model.EllipticCurve; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaEllipticCurveDeriveTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTestFile.py", + this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(PrivateKeyContext.class); + assertThat(value).isInstanceOf(Curve.class); + assertThat(value.asString()).isEqualTo("SECP256R1"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // PrivateKey + INode privateKeyNode = nodes.get(0); + assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); + assertThat(privateKeyNode.getChildren()).hasSize(2); + assertThat(privateKeyNode.asString()).isEqualTo("EC-secp256r1"); + + // KeyGeneration under PrivateKey + INode keyGenerationNode = privateKeyNode.getChildren().get(KeyGeneration.class); + assertThat(keyGenerationNode).isNotNull(); + assertThat(keyGenerationNode.getChildren()).isEmpty(); + assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); + + // PublicKeyEncryption under PrivateKey + INode publicKeyEncryptionNode = privateKeyNode.getChildren().get(PublicKeyEncryption.class); + assertThat(publicKeyEncryptionNode).isNotNull(); + assertThat(publicKeyEncryptionNode.getChildren()).hasSize(2); + assertThat(publicKeyEncryptionNode.asString()).isEqualTo("EC-secp256r1"); + + // EllipticCurve under PublicKeyEncryption under PrivateKey + INode ellipticCurveNode = publicKeyEncryptionNode.getChildren().get(EllipticCurve.class); + assertThat(ellipticCurveNode).isNotNull(); + assertThat(ellipticCurveNode.getChildren()).isEmpty(); + assertThat(ellipticCurveNode.asString()).isEqualTo("secp256r1"); + + // Oid under PublicKeyEncryption under PrivateKey + INode oidNode = publicKeyEncryptionNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("1.2.840.10045.2.1"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTest.java new file mode 100644 index 000000000..c073991dc --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTest.java @@ -0,0 +1,206 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.asymmetric.EllipticCurve; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.Curve; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.context.KeyAgreementContext; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.EllipticCurve; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyAgreement; +import com.ibm.mapper.model.KeyDerivationFunction; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyDerivation; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaEllipticCurveKeyExchangeTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTestFile.py", + this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 0) { + /* + * Detection Store + */ + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(PrivateKeyContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(Curve.class); + assertThat(value0.asString()).isEqualTo("SECP384R1"); + + DetectionStore store_1 = + getStoreOfValueType(Algorithm.class, detectionStore.getChildren()); + assertThat(store_1.getDetectionValues()).hasSize(1); + assertThat(store_1.getDetectionValueContext()).isInstanceOf(KeyAgreementContext.class); + IValue value0_1 = store_1.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(Algorithm.class); + assertThat(value0_1.asString()).isEqualTo("ECDH"); + + /* + * Translation + */ + + assertThat(nodes).hasSize(1); + + // PrivateKey + INode privateKeyNode = nodes.get(0); + assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); + assertThat(privateKeyNode.getChildren()).hasSize(2); + assertThat(privateKeyNode.asString()).isEqualTo("EC-secp384r1"); + + // KeyGeneration under PrivateKey + INode keyGenerationNode = privateKeyNode.getChildren().get(KeyGeneration.class); + assertThat(keyGenerationNode).isNotNull(); + assertThat(keyGenerationNode.getChildren()).isEmpty(); + assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); + + // KeyAgreement under PrivateKey + INode keyAgreementNode = privateKeyNode.getChildren().get(KeyAgreement.class); + assertThat(keyAgreementNode).isNotNull(); + assertThat(keyAgreementNode.getChildren()).hasSize(3); + assertThat(keyAgreementNode.asString()).isEqualTo("ECDH"); + + // EllipticCurve under KeyAgreement under PrivateKey + INode ellipticCurveNode = keyAgreementNode.getChildren().get(EllipticCurve.class); + assertThat(ellipticCurveNode).isNotNull(); + assertThat(ellipticCurveNode.getChildren()).isEmpty(); + assertThat(ellipticCurveNode.asString()).isEqualTo("secp384r1"); + + // Oid under KeyAgreement under PrivateKey + INode oidNode = keyAgreementNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("1.3.132.1.12"); + + // KeyGeneration under PrivateKey + keyGenerationNode = keyAgreementNode.getChildren().get(KeyGeneration.class); + assertThat(keyGenerationNode).isNotNull(); + assertThat(keyGenerationNode.getChildren()).isEmpty(); + assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); + } else if (findingId == 1) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(Algorithm.class); + assertThat(value0.asString()).isEqualTo("SHA256"); + + DetectionStore store_1 = + getStoreOfValueType(KeySize.class, detectionStore.getChildren()); + assertThat(store_1.getDetectionValues()).hasSize(1); + assertThat(store_1.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0_1 = store_1.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(KeySize.class); + assertThat(value0_1.asString()).isEqualTo("256"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // KeyDerivationFunction + INode keyDerivationFunctionNode = nodes.get(0); + assertThat(keyDerivationFunctionNode.getKind()).isEqualTo(KeyDerivationFunction.class); + assertThat(keyDerivationFunctionNode.getChildren()).hasSize(3); + assertThat(keyDerivationFunctionNode.asString()).isEqualTo("HKDF-SHA-256"); + + // KeyDerivation under KeyDerivationFunction + INode keyDerivationNode = + keyDerivationFunctionNode.getChildren().get(KeyDerivation.class); + assertThat(keyDerivationNode).isNotNull(); + assertThat(keyDerivationNode.getChildren()).isEmpty(); + assertThat(keyDerivationNode.asString()).isEqualTo("KEYDERIVATION"); + + // KeyLength under KeyDerivationFunction + INode keyLengthNode = keyDerivationFunctionNode.getChildren().get(KeyLength.class); + assertThat(keyLengthNode).isNotNull(); + assertThat(keyLengthNode.getChildren()).isEmpty(); + assertThat(keyLengthNode.asString()).isEqualTo("256"); + + // MessageDigest under KeyDerivationFunction + INode messageDigestNode = + keyDerivationFunctionNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode).isNotNull(); + assertThat(messageDigestNode.getChildren()).hasSize(4); + assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); + + // Digest under MessageDigest under KeyDerivationFunction + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // BlockSize under MessageDigest under KeyDerivationFunction + INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("512"); + + // Oid under MessageDigest under KeyDerivationFunction + INode oidNode = messageDigestNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + + // DigestSize under MessageDigest under KeyDerivationFunction + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("256"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTest.java new file mode 100644 index 000000000..9c1f7d79c --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTest.java @@ -0,0 +1,52 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.asymmetric.EllipticCurve; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.mapper.model.INode; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.Ignore; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaEllipticCurveNumbersTest extends TestBase { + + @Ignore("In this testcase the name of a var is resolved, but not teh actual value.") + @Test + void test() { + PythonCheckVerifier.verifyNoIssue( + "src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTestFile.py", + this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + // TODO: + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSign2Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSign2Test.java new file mode 100644 index 000000000..84e4928eb --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSign2Test.java @@ -0,0 +1,185 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.asymmetric.EllipticCurve; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.EllipticCurve; +import com.ibm.mapper.model.ExtendableOutputFunction; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaEllipticCurveSign2Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSign2TestFile.py", + this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 0) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(PrivateKeyContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(KeyAction.class); + assertThat(value0.asString()).isEqualTo("GENERATION"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // PrivateKey + INode privateKeyNode = nodes.get(0); + assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); + assertThat(privateKeyNode.getChildren()).hasSize(2); + assertThat(privateKeyNode.asString()).isEqualTo("Ed25519"); + + // Signature under PrivateKey + INode signatureNode = privateKeyNode.getChildren().get(Signature.class); + assertThat(signatureNode).isNotNull(); + assertThat(signatureNode.getChildren()).hasSize(3); + assertThat(signatureNode.asString()).isEqualTo("Ed25519"); + + // MessageDigest under Signature under PrivateKey + INode messageDigestNode = signatureNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode).isNotNull(); + assertThat(messageDigestNode.getChildren()).hasSize(4); + assertThat(messageDigestNode.asString()).isEqualTo("SHA-512"); + + // Oid under MessageDigest under Signature under PrivateKey + INode oidNode = messageDigestNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.3"); + + // DigestSize under MessageDigest under Signature under PrivateKey + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("512"); + + // Digest under MessageDigest under Signature under PrivateKey + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // BlockSize under MessageDigest under Signature under PrivateKey + INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("1024"); + + // Oid under Signature under PrivateKey + INode oidNode1 = signatureNode.getChildren().get(Oid.class); + assertThat(oidNode1).isNotNull(); + assertThat(oidNode1.getChildren()).isEmpty(); + assertThat(oidNode1.asString()).isEqualTo("1.3.101.112"); + + // EllipticCurve under Signature under PrivateKey + INode ellipticCurveNode = signatureNode.getChildren().get(EllipticCurve.class); + assertThat(ellipticCurveNode).isNotNull(); + assertThat(ellipticCurveNode.getChildren()).isEmpty(); + assertThat(ellipticCurveNode.asString()).isEqualTo("Edwards25519"); + } else if (findingId == 1) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(PrivateKeyContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(KeyAction.class); + assertThat(value0.asString()).isEqualTo("GENERATION"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // PrivateKey + INode privateKeyNode = nodes.get(0); + assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); + assertThat(privateKeyNode.getChildren()).hasSize(2); + assertThat(privateKeyNode.asString()).isEqualTo("Ed448"); + + // Signature under PrivateKey + INode signatureNode = privateKeyNode.getChildren().get(Signature.class); + assertThat(signatureNode).isNotNull(); + assertThat(signatureNode.getChildren()).hasSize(3); + assertThat(signatureNode.asString()).isEqualTo("Ed448"); + + // MessageDigest under Signature under PrivateKey + INode messageDigestNode = + signatureNode.getChildren().get(ExtendableOutputFunction.class); + assertThat(messageDigestNode).isNotNull(); + assertThat(messageDigestNode.getChildren()).hasSize(2); + assertThat(messageDigestNode.asString()).isEqualTo("SHAKE256"); + + // Digest under MessageDigest under Signature under PrivateKey + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // Oid under Signature under PrivateKey + INode oidNode = signatureNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("1.3.101.113"); + + // EllipticCurve under Signature under PrivateKey + INode ellipticCurveNode = signatureNode.getChildren().get(EllipticCurve.class); + assertThat(ellipticCurveNode).isNotNull(); + assertThat(ellipticCurveNode.getChildren()).isEmpty(); + assertThat(ellipticCurveNode.asString()).isEqualTo("Edwards448"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSignTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSignTest.java new file mode 100644 index 000000000..e70a1c3df --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSignTest.java @@ -0,0 +1,158 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.asymmetric.EllipticCurve; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.Curve; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.EllipticCurve; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Sign; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaEllipticCurveSignTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSignTestFile.py", + this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(PrivateKeyContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(Curve.class); + assertThat(value0.asString()).isEqualTo("SECP384R1"); + + DetectionStore store_1 = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(store_1.getDetectionValues()).hasSize(2); + assertThat(store_1.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue value0_1 = store_1.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(SignatureAction.class); + assertThat(value0_1.asString()).isEqualTo("SIGN"); + + IValue value1_1 = store_1.getDetectionValues().get(1); + assertThat(value1_1).isInstanceOf(Algorithm.class); + assertThat(value1_1.asString()).isEqualTo("ECDSA"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // PrivateKey + INode privateKeyNode = nodes.get(0); + assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); + assertThat(privateKeyNode.getChildren()).hasSize(3); + assertThat(privateKeyNode.asString()).isEqualTo("EC-secp384r1"); + + // Signature under PrivateKey + INode signatureNode = privateKeyNode.getChildren().get(Signature.class); + assertThat(signatureNode).isNotNull(); + assertThat(signatureNode.getChildren()).hasSize(3); + assertThat(signatureNode.asString()).isEqualTo("ECDSA-secp384r1-SHA3-512"); + + // MessageDigest under Signature under PrivateKey + INode messageDigestNode = signatureNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode).isNotNull(); + assertThat(messageDigestNode.getChildren()).hasSize(4); + assertThat(messageDigestNode.asString()).isEqualTo("SHA3-512"); + + // BlockSize under MessageDigest under Signature under PrivateKey + INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("576"); + + // Oid under MessageDigest under Signature under PrivateKey + INode oidNode = messageDigestNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.10"); + + // DigestSize under MessageDigest under Signature under PrivateKey + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("512"); + + // Digest under MessageDigest under Signature under PrivateKey + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // Oid under Signature under PrivateKey + INode oidNode1 = signatureNode.getChildren().get(Oid.class); + assertThat(oidNode1).isNotNull(); + assertThat(oidNode1.getChildren()).isEmpty(); + assertThat(oidNode1.asString()).isEqualTo("2.16.840.1.101.3.4.3.12"); + + // EllipticCurve under Signature under PrivateKey + INode ellipticCurveNode = signatureNode.getChildren().get(EllipticCurve.class); + assertThat(ellipticCurveNode).isNotNull(); + assertThat(ellipticCurveNode.getChildren()).isEmpty(); + assertThat(ellipticCurveNode.asString()).isEqualTo("secp384r1"); + + // Sign under PrivateKey + INode signNode = privateKeyNode.getChildren().get(Sign.class); + assertThat(signNode).isNotNull(); + assertThat(signNode.getChildren()).isEmpty(); + assertThat(signNode.asString()).isEqualTo("SIGN"); + + // KeyGeneration under PrivateKey + INode keyGenerationNode = privateKeyNode.getChildren().get(KeyGeneration.class); + assertThat(keyGenerationNode).isNotNull(); + assertThat(keyGenerationNode.getChildren()).isEmpty(); + assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTest.java new file mode 100644 index 000000000..2941263c2 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTest.java @@ -0,0 +1,55 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.asymmetric.EllipticCurve; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.mapper.model.INode; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaEllipticCurveVerifyTest extends TestBase { + + // junit4 + @Disabled( + "Reenable once we have an approach to detect `verify` (either make it an entry\n" + + "point, or better handle file imports for depending detection rule)") + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTestFile.py", + this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + // TODO + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSADecryptTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSADecryptTest.java new file mode 100644 index 000000000..274f3bcd8 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSADecryptTest.java @@ -0,0 +1,253 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.asymmetric.RSA; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.MaskGenerationFunction; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.Padding; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.functionality.Decrypt; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaRSADecryptTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSADecryptTestFile.py", + this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 0) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(PrivateKeyContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(KeySize.class); + assertThat(value0.asString()).isEqualTo("1024"); + + DetectionStore store_1 = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + assertThat(store_1).isNotNull(); + assertThat(store_1.getDetectionValues()).hasSize(1); + assertThat(store_1.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0_1 = store_1.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(CipherAction.class); + assertThat(value0_1.asString()).isEqualTo("DECRYPT"); + + DetectionStore store_1_1 = + getStoreOfValueType(ValueAction.class, store_1.getChildren()); + assertThat(store_1_1).isNotNull(); + assertThat(store_1_1.getDetectionValues()).hasSize(1); + assertThat(store_1_1.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0_1_1 = store_1_1.getDetectionValues().get(0); + assertThat(value0_1_1).isInstanceOf(ValueAction.class); + assertThat(value0_1_1.asString()).isEqualTo("OAEP"); + + List> stores = + getStoresOfValueType(ValueAction.class, store_1_1.getChildren()); + assertThat(stores).isNotNull(); + for (DetectionStore s : stores) { + assertThat(s.getDetectionValues()).hasSize(1); + assertThat(s.getDetectionValueContext()) + .isInstanceOfAny(SignatureContext.class, DigestContext.class); + IValue v = s.getDetectionValues().get(0); + assertThat(v).isInstanceOf(ValueAction.class); + assertThat(v.asString()) + .satisfiesAnyOf( + str -> assertThat(str).isEqualTo("MGF1"), + str -> assertThat(str).isEqualTo("SHA256")); + + if (s.getDetectionValueContext().is(SignatureContext.class)) { + DetectionStore store_1_1_1_1 = + getStoreOfValueType(ValueAction.class, s.getChildren()); + assertThat(store_1_1_1_1).isNotNull(); + assertThat(store_1_1_1_1.getDetectionValues()).hasSize(1); + assertThat(store_1_1_1_1.getDetectionValueContext()) + .isInstanceOf(DigestContext.class); + IValue value0_1_1_1_1 = store_1_1_1_1.getDetectionValues().get(0); + assertThat(value0_1_1_1_1).isInstanceOf(ValueAction.class); + assertThat(value0_1_1_1_1.asString()).isEqualTo("SHA384"); + } + } + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // PrivateKey + INode privateKeyNode = nodes.get(0); + assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); + assertThat(privateKeyNode.getChildren()).hasSize(4); + assertThat(privateKeyNode.asString()).isEqualTo("RSA"); + + // KeyGeneration under PrivateKey + INode keyGenerationNode = privateKeyNode.getChildren().get(KeyGeneration.class); + assertThat(keyGenerationNode).isNotNull(); + assertThat(keyGenerationNode.getChildren()).isEmpty(); + assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); + + // PublicKeyEncryption under PrivateKey + INode pke = privateKeyNode.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + assertThat(pke.getChildren()).hasSize(2); + assertThat(pke.asString()).isEqualTo("RSA-OAEP"); + + // Oid under Signature under PrivateKey + INode oidNode = pke.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("1.2.840.113549.1.1.7"); + + // Padding under Signature under PrivateKey + INode paddingNode = pke.getChildren().get(Padding.class); + assertThat(paddingNode).isNotNull(); + assertThat(paddingNode.getChildren()).hasSize(2); + assertThat(paddingNode.asString()).isEqualTo("OAEP"); + + // MessageDigest under Padding under Signature under PrivateKey + INode messageDigestNode = paddingNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode).isNotNull(); + assertThat(messageDigestNode.getChildren()).hasSize(4); + assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); + + // Oid under MessageDigest under Padding under Signature under PrivateKey + INode oidNode1 = messageDigestNode.getChildren().get(Oid.class); + assertThat(oidNode1).isNotNull(); + assertThat(oidNode1.getChildren()).isEmpty(); + assertThat(oidNode1.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + + // Digest under MessageDigest under Padding under Signature under PrivateKey + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // BlockSize under MessageDigest under Padding under Signature under PrivateKey + INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("512"); + + // DigestSize under MessageDigest under Padding under Signature under PrivateKey + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("256"); + + // MaskGenerationFunction under Padding under Signature under PrivateKey + INode maskGenerationFunctionNode = + paddingNode.getChildren().get(MaskGenerationFunction.class); + assertThat(maskGenerationFunctionNode).isNotNull(); + assertThat(maskGenerationFunctionNode.getChildren()).hasSize(2); + assertThat(maskGenerationFunctionNode.asString()).isEqualTo("MGF1"); + + // Oid under MaskGenerationFunction under Padding under Signature under PrivateKey + INode oidNode2 = maskGenerationFunctionNode.getChildren().get(Oid.class); + assertThat(oidNode2).isNotNull(); + assertThat(oidNode2.getChildren()).isEmpty(); + assertThat(oidNode2.asString()).isEqualTo("1.2.840.113549.1.1.8"); + + // MessageDigest under MaskGenerationFunction under Padding under Signature under + // PrivateKey + INode messageDigestNode1 = + maskGenerationFunctionNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode1).isNotNull(); + assertThat(messageDigestNode1.getChildren()).hasSize(4); + assertThat(messageDigestNode1.asString()).isEqualTo("SHA-384"); + + // Oid under MessageDigest under MaskGenerationFunction under Padding under Signature + // under PrivateKey + INode oidNode3 = messageDigestNode1.getChildren().get(Oid.class); + assertThat(oidNode3).isNotNull(); + assertThat(oidNode3.getChildren()).isEmpty(); + assertThat(oidNode3.asString()).isEqualTo("2.16.840.1.101.3.4.2.2"); + + // Digest under MessageDigest under MaskGenerationFunction under Padding under Signature + // under PrivateKey + INode digestNode1 = messageDigestNode1.getChildren().get(Digest.class); + assertThat(digestNode1).isNotNull(); + assertThat(digestNode1.getChildren()).isEmpty(); + assertThat(digestNode1.asString()).isEqualTo("DIGEST"); + + // BlockSize under MessageDigest under MaskGenerationFunction under Padding under + // Signature under PrivateKey + INode blockSizeNode1 = messageDigestNode1.getChildren().get(BlockSize.class); + assertThat(blockSizeNode1).isNotNull(); + assertThat(blockSizeNode1.getChildren()).isEmpty(); + assertThat(blockSizeNode1.asString()).isEqualTo("1024"); + + // DigestSize under MessageDigest under MaskGenerationFunction under Padding under + // Signature under PrivateKey + INode digestSizeNode1 = messageDigestNode1.getChildren().get(DigestSize.class); + assertThat(digestSizeNode1).isNotNull(); + assertThat(digestSizeNode1.getChildren()).isEmpty(); + assertThat(digestSizeNode1.asString()).isEqualTo("384"); + + // Decrypt under PrivateKey + INode decryptNode = privateKeyNode.getChildren().get(Decrypt.class); + assertThat(decryptNode).isNotNull(); + assertThat(decryptNode.getChildren()).isEmpty(); + assertThat(decryptNode.asString()).isEqualTo("DECRYPT"); + + // KeyLength under PrivateKey + INode keyLengthNode = privateKeyNode.getChildren().get(KeyLength.class); + assertThat(keyLengthNode).isNotNull(); + assertThat(keyLengthNode.getChildren()).isEmpty(); + assertThat(keyLengthNode.asString()).isEqualTo("1024"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSANumbersTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSANumbersTest.java new file mode 100644 index 000000000..5321621ee --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSANumbersTest.java @@ -0,0 +1,142 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.asymmetric.RSA; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.model.context.PublicKeyContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.PublicKey; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaRSANumbersTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSANumbersTestFile.py", + this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + + if (findingId == 0) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(PublicKeyContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(KeyAction.class); + assertThat(value0.asString()).isEqualTo("GENERATION"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // PublicKey + INode publicKeyNode = nodes.get(0); + assertThat(publicKeyNode.getKind()).isEqualTo(PublicKey.class); + assertThat(publicKeyNode.getChildren()).hasSize(2); + assertThat(publicKeyNode.asString()).isEqualTo("RSA"); + + // PublicKeyEncryption under PublicKey + INode publicKeyEncryptionNode = + publicKeyNode.getChildren().get(PublicKeyEncryption.class); + assertThat(publicKeyEncryptionNode).isNotNull(); + assertThat(publicKeyEncryptionNode.getChildren()).hasSize(1); + assertThat(publicKeyEncryptionNode.asString()).isEqualTo("RSA"); + + // Oid under PublicKeyEncryption under PublicKey + INode oidNode = publicKeyEncryptionNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("1.2.840.113549.1.1.1"); + + // KeyGeneration under PublicKey + INode keyGenerationNode = publicKeyNode.getChildren().get(KeyGeneration.class); + assertThat(keyGenerationNode).isNotNull(); + assertThat(keyGenerationNode.getChildren()).isEmpty(); + assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); + } else if (findingId == 1) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(PrivateKeyContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(KeyAction.class); + assertThat(value0.asString()).isEqualTo("GENERATION"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // PrivateKey + INode privateKeyNode = nodes.get(0); + assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); + assertThat(privateKeyNode.getChildren()).hasSize(2); + assertThat(privateKeyNode.asString()).isEqualTo("RSA"); + + // PublicKeyEncryption under PrivateKey + INode publicKeyEncryptionNode = + privateKeyNode.getChildren().get(PublicKeyEncryption.class); + assertThat(publicKeyEncryptionNode).isNotNull(); + assertThat(publicKeyEncryptionNode.getChildren()).hasSize(1); + assertThat(publicKeyEncryptionNode.asString()).isEqualTo("RSA"); + + // Oid under PublicKeyEncryption under PrivateKey + INode oidNode = publicKeyEncryptionNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("1.2.840.113549.1.1.1"); + + // KeyGeneration under PrivateKey + INode keyGenerationNode = privateKeyNode.getChildren().get(KeyGeneration.class); + assertThat(keyGenerationNode).isNotNull(); + assertThat(keyGenerationNode.getChildren()).isEmpty(); + assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSASign1Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSASign1Test.java new file mode 100644 index 000000000..25c86fe9c --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSASign1Test.java @@ -0,0 +1,231 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.asymmetric.RSA; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.MaskGenerationFunction; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.ProbabilisticSignatureScheme; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Sign; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaRSASign1Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSASign1TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(PrivateKeyContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(KeySize.class); + assertThat(value0.asString()).isEqualTo("2048"); + + DetectionStore store_1 = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(store_1.getDetectionValues()).hasSize(1); + assertThat(store_1.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue value0_1 = store_1.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(SignatureAction.class); + assertThat(value0_1.asString()).isEqualTo("SIGN"); + + DetectionStore store_1_1 = + getStoreOfValueType(ValueAction.class, store_1.getChildren()); + assertThat(store_1_1.getDetectionValues()).hasSize(1); + assertThat(store_1_1.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue value0_1_1 = store_1_1.getDetectionValues().get(0); + assertThat(value0_1_1).isInstanceOf(ValueAction.class); + assertThat(value0_1_1.asString()).isEqualTo("RSA-PSS"); + + DetectionStore store_1_1_1 = + getStoreOfValueType(ValueAction.class, store_1_1.getChildren()); + assertThat(store_1_1_1.getDetectionValues()).hasSize(1); + assertThat(store_1_1_1.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue value0_1_1_1 = store_1_1_1.getDetectionValues().get(0); + assertThat(value0_1_1_1).isInstanceOf(ValueAction.class); + assertThat(value0_1_1_1.asString()).isEqualTo("MGF1"); + + DetectionStore store_1_1_1_1 = + getStoreOfValueType(ValueAction.class, store_1_1_1.getChildren()); + assertThat(store_1_1_1_1.getDetectionValues()).hasSize(1); + assertThat(store_1_1_1_1.getDetectionValueContext()).isInstanceOf(DigestContext.class); + IValue value0_1_1_1_1 = store_1_1_1_1.getDetectionValues().get(0); + assertThat(value0_1_1_1_1).isInstanceOf(ValueAction.class); + assertThat(value0_1_1_1_1.asString()).isEqualTo("SHA256"); + + /* + * Translation + */ + + assertThat(nodes).hasSize(1); + + // PrivateKey + INode privateKeyNode = nodes.get(0); + assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); + assertThat(privateKeyNode.getChildren()).hasSize(4); + assertThat(privateKeyNode.asString()).isEqualTo("RSA"); + + // Sign under PrivateKey + INode signNode = privateKeyNode.getChildren().get(Sign.class); + assertThat(signNode).isNotNull(); + assertThat(signNode.getChildren()).isEmpty(); + assertThat(signNode.asString()).isEqualTo("SIGN"); + + // KeyGeneration under PrivateKey + INode keyGenerationNode = privateKeyNode.getChildren().get(KeyGeneration.class); + assertThat(keyGenerationNode).isNotNull(); + assertThat(keyGenerationNode.getChildren()).isEmpty(); + assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); + + // ProbabilisticSignatureScheme under PrivateKey + INode probabilisticSignatureSchemeNode = + privateKeyNode.getChildren().get(ProbabilisticSignatureScheme.class); + assertThat(probabilisticSignatureSchemeNode).isNotNull(); + assertThat(probabilisticSignatureSchemeNode.getChildren()).hasSize(3); + assertThat(probabilisticSignatureSchemeNode.asString()).isEqualTo("RSA-PSS"); + + // MaskGenerationFunction under ProbabilisticSignatureScheme under PrivateKey + INode maskGenerationFunctionNode = + probabilisticSignatureSchemeNode.getChildren().get(MaskGenerationFunction.class); + assertThat(maskGenerationFunctionNode).isNotNull(); + assertThat(maskGenerationFunctionNode.getChildren()).hasSize(2); + assertThat(maskGenerationFunctionNode.asString()).isEqualTo("MGF1"); + + // MessageDigest under MaskGenerationFunction under ProbabilisticSignatureScheme under + // PrivateKey + INode messageDigestNode = maskGenerationFunctionNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode).isNotNull(); + assertThat(messageDigestNode.getChildren()).hasSize(4); + assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); + + // BlockSize under MessageDigest under MaskGenerationFunction under + // ProbabilisticSignatureScheme under PrivateKey + INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("512"); + + // DigestSize under MessageDigest under MaskGenerationFunction under + // ProbabilisticSignatureScheme under PrivateKey + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("256"); + + // Oid under MessageDigest under MaskGenerationFunction under ProbabilisticSignatureScheme + // under PrivateKey + INode oidNode1 = messageDigestNode.getChildren().get(Oid.class); + assertThat(oidNode1).isNotNull(); + assertThat(oidNode1.getChildren()).isEmpty(); + assertThat(oidNode1.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + + // Digest under MessageDigest under MaskGenerationFunction under + // ProbabilisticSignatureScheme under PrivateKey + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // Oid under MaskGenerationFunction under ProbabilisticSignatureScheme under PrivateKey + INode oidNode2 = maskGenerationFunctionNode.getChildren().get(Oid.class); + assertThat(oidNode2).isNotNull(); + assertThat(oidNode2.getChildren()).isEmpty(); + assertThat(oidNode2.asString()).isEqualTo("1.2.840.113549.1.1.8"); + + // MessageDigest under ProbabilisticSignatureScheme under PrivateKey + INode messageDigestNode1 = + probabilisticSignatureSchemeNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode1).isNotNull(); + assertThat(messageDigestNode1.getChildren()).hasSize(4); + assertThat(messageDigestNode1.asString()).isEqualTo("SHA-384"); + + // BlockSize under MessageDigest under ProbabilisticSignatureScheme under PrivateKey + INode blockSizeNode1 = messageDigestNode1.getChildren().get(BlockSize.class); + assertThat(blockSizeNode1).isNotNull(); + assertThat(blockSizeNode1.getChildren()).isEmpty(); + assertThat(blockSizeNode1.asString()).isEqualTo("1024"); + + // DigestSize under MessageDigest under ProbabilisticSignatureScheme under PrivateKey + INode digestSizeNode1 = messageDigestNode1.getChildren().get(DigestSize.class); + assertThat(digestSizeNode1).isNotNull(); + assertThat(digestSizeNode1.getChildren()).isEmpty(); + assertThat(digestSizeNode1.asString()).isEqualTo("384"); + + // Oid under MessageDigest under ProbabilisticSignatureScheme under PrivateKey + INode oidNode3 = messageDigestNode1.getChildren().get(Oid.class); + assertThat(oidNode3).isNotNull(); + assertThat(oidNode3.getChildren()).isEmpty(); + assertThat(oidNode3.asString()).isEqualTo("2.16.840.1.101.3.4.2.2"); + + // Digest under MessageDigest under ProbabilisticSignatureScheme under PrivateKey + INode digestNode1 = messageDigestNode1.getChildren().get(Digest.class); + assertThat(digestNode1).isNotNull(); + assertThat(digestNode1.getChildren()).isEmpty(); + assertThat(digestNode1.asString()).isEqualTo("DIGEST"); + + // Oid under ProbabilisticSignatureScheme under PrivateKey + INode oidNode4 = probabilisticSignatureSchemeNode.getChildren().get(Oid.class); + assertThat(oidNode4).isNotNull(); + assertThat(oidNode4.getChildren()).isEmpty(); + assertThat(oidNode4.asString()).isEqualTo("1.2.840.113549.1.1.10"); + + // KeyLength under PrivateKey + INode keyLengthNode = privateKeyNode.getChildren().get(KeyLength.class); + assertThat(keyLengthNode).isNotNull(); + assertThat(keyLengthNode.getChildren()).isEmpty(); + assertThat(keyLengthNode.asString()).isEqualTo("2048"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSASign2Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSASign2Test.java new file mode 100644 index 000000000..c39328fa6 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSASign2Test.java @@ -0,0 +1,170 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.asymmetric.RSA; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Sign; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaRSASign2Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSASign2TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(PrivateKeyContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(KeySize.class); + assertThat(value0.asString()).isEqualTo("2048"); + + DetectionStore store_1 = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(store_1.getDetectionValues()).hasSize(1); + assertThat(store_1.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue value0_1 = store_1.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(SignatureAction.class); + assertThat(value0_1.asString()).isEqualTo("SIGN"); + + List> stores = + getStoresOfValueType(ValueAction.class, store_1.getChildren()); + assertThat(stores).isNotNull(); + for (DetectionStore store : stores) { + assertThat(store.getDetectionValues()).hasSize(1); + assertThat(store.getDetectionValueContext()) + .isInstanceOfAny(SignatureContext.class, DigestContext.class); + IValue v = store.getDetectionValues().get(0); + assertThat(v).isInstanceOf(ValueAction.class); + assertThat(v.asString()) + .satisfiesAnyOf( + s -> assertThat(s).isEqualTo("PKCS1v15"), + s -> assertThat(s).isEqualTo("SHA3_384")); + } + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // PrivateKey + INode privateKeyNode = nodes.get(0); + assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); + assertThat(privateKeyNode.getChildren()).hasSize(4); + assertThat(privateKeyNode.asString()).isEqualTo("RSA"); + + // KeyGeneration under PrivateKey + INode keyGenerationNode = privateKeyNode.getChildren().get(KeyGeneration.class); + assertThat(keyGenerationNode).isNotNull(); + assertThat(keyGenerationNode.getChildren()).isEmpty(); + assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); + + // Sign under PrivateKey + INode signNode = privateKeyNode.getChildren().get(Sign.class); + assertThat(signNode).isNotNull(); + assertThat(signNode.getChildren()).isEmpty(); + assertThat(signNode.asString()).isEqualTo("SIGN"); + + // KeyLength under PrivateKey + INode keyLengthNode = privateKeyNode.getChildren().get(KeyLength.class); + assertThat(keyLengthNode).isNotNull(); + assertThat(keyLengthNode.getChildren()).isEmpty(); + assertThat(keyLengthNode.asString()).isEqualTo("2048"); + + // Signature under PrivateKey + INode signatureNode = privateKeyNode.getChildren().get(Signature.class); + assertThat(signatureNode).isNotNull(); + assertThat(signatureNode.getChildren()).hasSize(2); + assertThat(signatureNode.asString()).isEqualTo("RSA-PKCS1-1.5-SHA3-384"); + + // MessageDigest under Signature under PrivateKey + INode messageDigestNode = signatureNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode).isNotNull(); + assertThat(messageDigestNode.getChildren()).hasSize(4); + assertThat(messageDigestNode.asString()).isEqualTo("SHA3-384"); + + // DigestSize under MessageDigest under Signature under PrivateKey + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("384"); + + // Oid under MessageDigest under Signature under PrivateKey + INode oidNode = messageDigestNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.9"); + + // BlockSize under MessageDigest under Signature under PrivateKey + INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("832"); + + // Digest under MessageDigest under Signature under PrivateKey + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // Oid under Signature under PrivateKey + INode oidNode1 = signatureNode.getChildren().get(Oid.class); + assertThat(oidNode1).isNotNull(); + assertThat(oidNode1.getChildren()).isEmpty(); + assertThat(oidNode1.asString()).isEqualTo("2.16.840.1.101.3.4.3.15"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaFernetDecryptTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaFernetDecryptTest.java new file mode 100644 index 000000000..19f7d28cd --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaFernetDecryptTest.java @@ -0,0 +1,200 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.fernet; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.mapper.model.AuthenticatedEncryption; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.Mac; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Mode; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.Padding; +import com.ibm.mapper.model.SecretKey; +import com.ibm.mapper.model.functionality.Decrypt; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Tag; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaFernetDecryptTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/fernet/PycaFernetDecryptTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(KeyContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(KeyAction.class); + assertThat(value0.asString()).isEqualTo("GENERATION"); + + DetectionStore store_1 = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + assertThat(store_1.getDetectionValues()).hasSize(1); + assertThat(store_1.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0_1 = store_1.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(CipherAction.class); + assertThat(value0_1.asString()).isEqualTo("DECRYPT"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // SecretKey + INode secretKeyNode = nodes.get(0); + assertThat(secretKeyNode.getKind()).isEqualTo(SecretKey.class); + assertThat(secretKeyNode.getChildren()).hasSize(3); + assertThat(secretKeyNode.asString()).isEqualTo("Fernet"); + + // KeyGeneration under SecretKey + INode keyGenerationNode = secretKeyNode.getChildren().get(KeyGeneration.class); + assertThat(keyGenerationNode).isNotNull(); + assertThat(keyGenerationNode.getChildren()).isEmpty(); + assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); + + // Decrypt under SecretKey + INode decryptNode = secretKeyNode.getChildren().get(Decrypt.class); + assertThat(decryptNode).isNotNull(); + assertThat(decryptNode.getChildren()).isEmpty(); + assertThat(decryptNode.asString()).isEqualTo("DECRYPT"); + + // AuthenticatedEncryption under SecretKey + INode authenticatedEncryptionNode = + secretKeyNode.getChildren().get(AuthenticatedEncryption.class); + assertThat(authenticatedEncryptionNode).isNotNull(); + assertThat(authenticatedEncryptionNode.getChildren()).hasSize(2); + assertThat(authenticatedEncryptionNode.asString()).isEqualTo("Fernet"); + + // Mac under AuthenticatedEncryption under SecretKey + INode macNode = authenticatedEncryptionNode.getChildren().get(Mac.class); + assertThat(macNode).isNotNull(); + assertThat(macNode.getChildren()).hasSize(3); + assertThat(macNode.asString()).isEqualTo("HMAC-SHA-256"); + + // MessageDigest under Mac under AuthenticatedEncryption under SecretKey + INode messageDigestNode = macNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode).isNotNull(); + assertThat(messageDigestNode.getChildren()).hasSize(4); + assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); + + // Digest under MessageDigest under Mac under AuthenticatedEncryption under SecretKey + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // BlockSize under MessageDigest under Mac under AuthenticatedEncryption under SecretKey + INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("512"); + + // DigestSize under MessageDigest under Mac under AuthenticatedEncryption under SecretKey + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("256"); + + // Oid under MessageDigest under Mac under AuthenticatedEncryption under SecretKey + INode oidNode = messageDigestNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + + // Tag under Mac under AuthenticatedEncryption under SecretKey + INode tagNode = macNode.getChildren().get(Tag.class); + assertThat(tagNode).isNotNull(); + assertThat(tagNode.getChildren()).isEmpty(); + assertThat(tagNode.asString()).isEqualTo("TAG"); + + // Oid under Mac under AuthenticatedEncryption under SecretKey + INode oidNode1 = macNode.getChildren().get(Oid.class); + assertThat(oidNode1).isNotNull(); + assertThat(oidNode1.getChildren()).isEmpty(); + assertThat(oidNode1.asString()).isEqualTo("1.2.840.113549.2.9"); + + // BlockCipher under AuthenticatedEncryption under SecretKey + INode blockCipherNode = authenticatedEncryptionNode.getChildren().get(BlockCipher.class); + assertThat(blockCipherNode).isNotNull(); + assertThat(blockCipherNode.getChildren()).hasSize(5); + assertThat(blockCipherNode.asString()).isEqualTo("AES-128-CBC-PKCS7"); + + // Mode under BlockCipher under AuthenticatedEncryption under SecretKey + INode modeNode = blockCipherNode.getChildren().get(Mode.class); + assertThat(modeNode).isNotNull(); + assertThat(modeNode.getChildren()).isEmpty(); + assertThat(modeNode.asString()).isEqualTo("CBC"); + + // BlockSize under BlockCipher under AuthenticatedEncryption under SecretKey + INode blockSizeNode1 = blockCipherNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode1).isNotNull(); + assertThat(blockSizeNode1.getChildren()).isEmpty(); + assertThat(blockSizeNode1.asString()).isEqualTo("128"); + + // Padding under BlockCipher under AuthenticatedEncryption under SecretKey + INode paddingNode = blockCipherNode.getChildren().get(Padding.class); + assertThat(paddingNode).isNotNull(); + assertThat(paddingNode.getChildren()).isEmpty(); + assertThat(paddingNode.asString()).isEqualTo("PKCS7"); + + // KeyLength under BlockCipher under AuthenticatedEncryption under SecretKey + INode keyLengthNode = blockCipherNode.getChildren().get(KeyLength.class); + assertThat(keyLengthNode).isNotNull(); + assertThat(keyLengthNode.getChildren()).isEmpty(); + assertThat(keyLengthNode.asString()).isEqualTo("128"); + + // Oid under BlockCipher under AuthenticatedEncryption under SecretKey + INode oidNode2 = blockCipherNode.getChildren().get(Oid.class); + assertThat(oidNode2).isNotNull(); + assertThat(oidNode2.getChildren()).isEmpty(); + assertThat(oidNode2.asString()).isEqualTo("2.16.840.1.101.3.4.1.2"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaFernetEncryptTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaFernetEncryptTest.java new file mode 100644 index 000000000..5ad5aa112 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaFernetEncryptTest.java @@ -0,0 +1,200 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.fernet; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.mapper.model.AuthenticatedEncryption; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.Mac; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Mode; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.Padding; +import com.ibm.mapper.model.SecretKey; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.Encrypt; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Tag; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaFernetEncryptTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/fernet/PycaFernetEncryptTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(KeyContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(KeyAction.class); + assertThat(value0.asString()).isEqualTo("GENERATION"); + + DetectionStore store_1 = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + assertThat(store_1.getDetectionValues()).hasSize(1); + assertThat(store_1.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0_1 = store_1.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(CipherAction.class); + assertThat(value0_1.asString()).isEqualTo("ENCRYPT"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // SecretKey + INode secretKeyNode = nodes.get(0); + assertThat(secretKeyNode.getKind()).isEqualTo(SecretKey.class); + assertThat(secretKeyNode.getChildren()).hasSize(3); + assertThat(secretKeyNode.asString()).isEqualTo("Fernet"); + + // Encrypt under SecretKey + INode encryptNode = secretKeyNode.getChildren().get(Encrypt.class); + assertThat(encryptNode).isNotNull(); + assertThat(encryptNode.getChildren()).isEmpty(); + assertThat(encryptNode.asString()).isEqualTo("ENCRYPT"); + + // KeyGeneration under SecretKey + INode keyGenerationNode = secretKeyNode.getChildren().get(KeyGeneration.class); + assertThat(keyGenerationNode).isNotNull(); + assertThat(keyGenerationNode.getChildren()).isEmpty(); + assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); + + // AuthenticatedEncryption under SecretKey + INode authenticatedEncryptionNode = + secretKeyNode.getChildren().get(AuthenticatedEncryption.class); + assertThat(authenticatedEncryptionNode).isNotNull(); + assertThat(authenticatedEncryptionNode.getChildren()).hasSize(2); + assertThat(authenticatedEncryptionNode.asString()).isEqualTo("Fernet"); + + // Mac under AuthenticatedEncryption under SecretKey + INode macNode = authenticatedEncryptionNode.getChildren().get(Mac.class); + assertThat(macNode).isNotNull(); + assertThat(macNode.getChildren()).hasSize(3); + assertThat(macNode.asString()).isEqualTo("HMAC-SHA-256"); + + // MessageDigest under Mac under AuthenticatedEncryption under SecretKey + INode messageDigestNode = macNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode).isNotNull(); + assertThat(messageDigestNode.getChildren()).hasSize(4); + assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); + + // Digest under MessageDigest under Mac under AuthenticatedEncryption under SecretKey + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // Oid under MessageDigest under Mac under AuthenticatedEncryption under SecretKey + INode oidNode = messageDigestNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + + // BlockSize under MessageDigest under Mac under AuthenticatedEncryption under SecretKey + INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("512"); + + // DigestSize under MessageDigest under Mac under AuthenticatedEncryption under SecretKey + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("256"); + + // Tag under Mac under AuthenticatedEncryption under SecretKey + INode tagNode = macNode.getChildren().get(Tag.class); + assertThat(tagNode).isNotNull(); + assertThat(tagNode.getChildren()).isEmpty(); + assertThat(tagNode.asString()).isEqualTo("TAG"); + + // Oid under Mac under AuthenticatedEncryption under SecretKey + INode oidNode1 = macNode.getChildren().get(Oid.class); + assertThat(oidNode1).isNotNull(); + assertThat(oidNode1.getChildren()).isEmpty(); + assertThat(oidNode1.asString()).isEqualTo("1.2.840.113549.2.9"); + + // BlockCipher under AuthenticatedEncryption under SecretKey + INode blockCipherNode = authenticatedEncryptionNode.getChildren().get(BlockCipher.class); + assertThat(blockCipherNode).isNotNull(); + assertThat(blockCipherNode.getChildren()).hasSize(5); + assertThat(blockCipherNode.asString()).isEqualTo("AES-128-CBC-PKCS7"); + + // Mode under BlockCipher under AuthenticatedEncryption under SecretKey + INode modeNode = blockCipherNode.getChildren().get(Mode.class); + assertThat(modeNode).isNotNull(); + assertThat(modeNode.getChildren()).isEmpty(); + assertThat(modeNode.asString()).isEqualTo("CBC"); + + // Padding under BlockCipher under AuthenticatedEncryption under SecretKey + INode paddingNode = blockCipherNode.getChildren().get(Padding.class); + assertThat(paddingNode).isNotNull(); + assertThat(paddingNode.getChildren()).isEmpty(); + assertThat(paddingNode.asString()).isEqualTo("PKCS7"); + + // KeyLength under BlockCipher under AuthenticatedEncryption under SecretKey + INode keyLengthNode = blockCipherNode.getChildren().get(KeyLength.class); + assertThat(keyLengthNode).isNotNull(); + assertThat(keyLengthNode.getChildren()).isEmpty(); + assertThat(keyLengthNode.asString()).isEqualTo("128"); + + // Oid under BlockCipher under AuthenticatedEncryption under SecretKey + INode oidNode2 = blockCipherNode.getChildren().get(Oid.class); + assertThat(oidNode2).isNotNull(); + assertThat(oidNode2.getChildren()).isEmpty(); + assertThat(oidNode2.asString()).isEqualTo("2.16.840.1.101.3.4.1.2"); + + // BlockSize under BlockCipher under AuthenticatedEncryption under SecretKey + INode blockSizeNode1 = blockCipherNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode1).isNotNull(); + assertThat(blockSizeNode1.getChildren()).isEmpty(); + assertThat(blockSizeNode1.asString()).isEqualTo("128"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaMultiFernetTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaMultiFernetTest.java new file mode 100644 index 000000000..ac90ea5a5 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaMultiFernetTest.java @@ -0,0 +1,211 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.fernet; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.mapper.model.AuthenticatedEncryption; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.Mac; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Mode; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.Padding; +import com.ibm.mapper.model.SecretKey; +import com.ibm.mapper.model.functionality.Decrypt; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.Encrypt; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Tag; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaMultiFernetTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/fernet/PycaMultiFernetTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(KeyContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(KeyAction.class); + assertThat(value0.asString()).isEqualTo("GENERATION"); + + DetectionStore store_2 = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + assertThat(store_2.getDetectionValues()).hasSize(1); + assertThat(store_2.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0_2 = store_2.getDetectionValues().get(0); + assertThat(value0_2).isInstanceOf(CipherAction.class); + assertThat(value0_2.asString()) + .satisfiesAnyOf( + s -> assertThat(s).isEqualTo("ENCRYPT"), + s -> assertThat(s).isEqualTo("DECRYPT")); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // SecretKey + INode secretKeyNode = nodes.get(0); + assertThat(secretKeyNode.getKind()).isEqualTo(SecretKey.class); + assertThat(secretKeyNode.getChildren()).hasSize(4); + assertThat(secretKeyNode.asString()).isEqualTo("Fernet"); + + // Decrypt under SecretKey + INode decryptNode = secretKeyNode.getChildren().get(Decrypt.class); + assertThat(decryptNode).isNotNull(); + assertThat(decryptNode.getChildren()).isEmpty(); + assertThat(decryptNode.asString()).isEqualTo("DECRYPT"); + + // AuthenticatedEncryption under SecretKey + INode authenticatedEncryptionNode = + secretKeyNode.getChildren().get(AuthenticatedEncryption.class); + assertThat(authenticatedEncryptionNode).isNotNull(); + assertThat(authenticatedEncryptionNode.getChildren()).hasSize(2); + assertThat(authenticatedEncryptionNode.asString()).isEqualTo("Fernet"); + + // BlockCipher under AuthenticatedEncryption under SecretKey + INode blockCipherNode = authenticatedEncryptionNode.getChildren().get(BlockCipher.class); + assertThat(blockCipherNode).isNotNull(); + assertThat(blockCipherNode.getChildren()).hasSize(5); + assertThat(blockCipherNode.asString()).isEqualTo("AES-128-CBC-PKCS7"); + + // BlockSize under BlockCipher under AuthenticatedEncryption under SecretKey + INode blockSizeNode = blockCipherNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("128"); + + // KeyLength under BlockCipher under AuthenticatedEncryption under SecretKey + INode keyLengthNode = blockCipherNode.getChildren().get(KeyLength.class); + assertThat(keyLengthNode).isNotNull(); + assertThat(keyLengthNode.getChildren()).isEmpty(); + assertThat(keyLengthNode.asString()).isEqualTo("128"); + + // Oid under BlockCipher under AuthenticatedEncryption under SecretKey + INode oidNode = blockCipherNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.1.2"); + + // Mode under BlockCipher under AuthenticatedEncryption under SecretKey + INode modeNode = blockCipherNode.getChildren().get(Mode.class); + assertThat(modeNode).isNotNull(); + assertThat(modeNode.getChildren()).isEmpty(); + assertThat(modeNode.asString()).isEqualTo("CBC"); + + // Padding under BlockCipher under AuthenticatedEncryption under SecretKey + INode paddingNode = blockCipherNode.getChildren().get(Padding.class); + assertThat(paddingNode).isNotNull(); + assertThat(paddingNode.getChildren()).isEmpty(); + assertThat(paddingNode.asString()).isEqualTo("PKCS7"); + + // Mac under AuthenticatedEncryption under SecretKey + INode macNode = authenticatedEncryptionNode.getChildren().get(Mac.class); + assertThat(macNode).isNotNull(); + assertThat(macNode.getChildren()).hasSize(3); + assertThat(macNode.asString()).isEqualTo("HMAC-SHA-256"); + + // Tag under Mac under AuthenticatedEncryption under SecretKey + INode tagNode = macNode.getChildren().get(Tag.class); + assertThat(tagNode).isNotNull(); + assertThat(tagNode.getChildren()).isEmpty(); + assertThat(tagNode.asString()).isEqualTo("TAG"); + + // Oid under Mac under AuthenticatedEncryption under SecretKey + oidNode = macNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("1.2.840.113549.2.9"); + + // MessageDigest under Mac under AuthenticatedEncryption under SecretKey + INode messageDigestNode = macNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode).isNotNull(); + assertThat(messageDigestNode.getChildren()).hasSize(4); + assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); + + // BlockSize under MessageDigest under Mac under AuthenticatedEncryption under SecretKey + INode blockSizeNode1 = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode1).isNotNull(); + assertThat(blockSizeNode1.getChildren()).isEmpty(); + assertThat(blockSizeNode1.asString()).isEqualTo("512"); + + // Oid under MessageDigest under Mac under AuthenticatedEncryption under SecretKey + INode oidNode1 = messageDigestNode.getChildren().get(Oid.class); + assertThat(oidNode1).isNotNull(); + assertThat(oidNode1.getChildren()).isEmpty(); + assertThat(oidNode1.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + + // DigestSize under MessageDigest under Mac under AuthenticatedEncryption under + // SecretKey + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("256"); + + // Digest under MessageDigest under Mac under AuthenticatedEncryption under SecretKey + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // Encrypt under SecretKey + INode encryptNode = secretKeyNode.getChildren().get(Encrypt.class); + assertThat(encryptNode).isNotNull(); + assertThat(encryptNode.getChildren()).isEmpty(); + assertThat(encryptNode.asString()).isEqualTo("ENCRYPT"); + + // KeyGeneration under SecretKey + INode keyGenerationNode = secretKeyNode.getChildren().get(KeyGeneration.class); + assertThat(keyGenerationNode).isNotNull(); + assertThat(keyGenerationNode.getChildren()).isEmpty(); + assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/hash/PycaHashDirectTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/hash/PycaHashDirectTest.java new file mode 100644 index 000000000..0e57b8a00 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/hash/PycaHashDirectTest.java @@ -0,0 +1,102 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaHashDirectTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/hash/PycaHashDirectTest.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(Algorithm.class); + assertThat(value0.asString()).isEqualTo("SHA256"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // MessageDigest (SHA256) + INode messageDigestNode = nodes.get(0); + assertThat(messageDigestNode.getKind()).isEqualTo(MessageDigest.class); + assertThat(messageDigestNode.getChildren()).hasSize(4); + assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); + + // DigestSize under MessageDigest + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("256"); + + // BlockSize under MessageDigest + INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("512"); + + // Oid under MessageDigest + INode oidNode = messageDigestNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + + // Digest functionality under MessageDigest + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaConcatKDFHMACTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaConcatKDFHMACTest.java new file mode 100644 index 000000000..2e7bd5711 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaConcatKDFHMACTest.java @@ -0,0 +1,133 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.kdf; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyDerivationFunction; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyDerivation; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaConcatKDFHMACTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/kdf/PycaConcatKDFHMACTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(Algorithm.class); + assertThat(value0.asString()).isEqualTo("SHA256"); + + DetectionStore store_1 = + getStoreOfValueType(KeySize.class, detectionStore.getChildren()); + assertThat(store_1.getDetectionValues()).hasSize(1); + assertThat(store_1.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0_1 = store_1.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(KeySize.class); + assertThat(value0_1.asString()).isEqualTo("256"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // Mac + INode macNode = nodes.get(0); + assertThat(macNode.getKind()).isEqualTo(KeyDerivationFunction.class); + assertThat(macNode.getChildren()).hasSize(3); + assertThat(macNode.asString()).isEqualTo("ConcatenationKDF"); + + // MessageDigest under Mac + INode messageDigestNode = macNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode).isNotNull(); + assertThat(messageDigestNode.getChildren()).hasSize(4); + assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); + + // DigestSize under MessageDigest under Mac + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("256"); + + // Oid under MessageDigest under Mac + INode oidNode = messageDigestNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + + // Digest under MessageDigest under Mac + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // BlockSize under MessageDigest under Mac + INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("512"); + + // KeyDerivation under Mac + INode keyDerivationNode = macNode.getChildren().get(KeyDerivation.class); + assertThat(keyDerivationNode).isNotNull(); + assertThat(keyDerivationNode.getChildren()).isEmpty(); + assertThat(keyDerivationNode.asString()).isEqualTo("KEYDERIVATION"); + + // KeyLength under Mac + INode keyLengthNode = macNode.getChildren().get(KeyLength.class); + assertThat(keyLengthNode).isNotNull(); + assertThat(keyLengthNode.getChildren()).isEmpty(); + assertThat(keyLengthNode.asString()).isEqualTo("256"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaConcatKDFHashTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaConcatKDFHashTest.java new file mode 100644 index 000000000..e10a0c5f2 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaConcatKDFHashTest.java @@ -0,0 +1,131 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.kdf; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyDerivationFunction; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyDerivation; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaConcatKDFHashTest extends TestBase { + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/kdf/PycaConcatKDFHashTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(Algorithm.class); + assertThat(value0.asString()).isEqualTo("SHA256"); + + DetectionStore store_1 = + getStoreOfValueType(KeySize.class, detectionStore.getChildren()); + assertThat(store_1.getDetectionValues()).hasSize(1); + assertThat(store_1.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0_1 = store_1.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(KeySize.class); + assertThat(value0_1.asString()).isEqualTo("512"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + // KeyDerivationFunction + INode keyDerivationFunctionNode = nodes.get(0); + assertThat(keyDerivationFunctionNode.getKind()).isEqualTo(KeyDerivationFunction.class); + assertThat(keyDerivationFunctionNode.getChildren()).hasSize(3); + assertThat(keyDerivationFunctionNode.asString()).isEqualTo("ConcatenationKDF"); + + // MessageDigest under KeyDerivationFunction + INode messageDigestNode = keyDerivationFunctionNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode).isNotNull(); + assertThat(messageDigestNode.getChildren()).hasSize(4); + assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); + + // BlockSize under MessageDigest under KeyDerivationFunction + INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("512"); + + // Digest under MessageDigest under KeyDerivationFunction + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // Oid under MessageDigest under KeyDerivationFunction + INode oidNode = messageDigestNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + + // DigestSize under MessageDigest under KeyDerivationFunction + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("256"); + + // KeyLength under KeyDerivationFunction + INode keyLengthNode = keyDerivationFunctionNode.getChildren().get(KeyLength.class); + assertThat(keyLengthNode).isNotNull(); + assertThat(keyLengthNode.getChildren()).isEmpty(); + assertThat(keyLengthNode.asString()).isEqualTo("512"); + + // KeyDerivation under KeyDerivationFunction + INode keyDerivationNode = keyDerivationFunctionNode.getChildren().get(KeyDerivation.class); + assertThat(keyDerivationNode).isNotNull(); + assertThat(keyDerivationNode.getChildren()).isEmpty(); + assertThat(keyDerivationNode.asString()).isEqualTo("KEYDERIVATION"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaHKDFExpandTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaHKDFExpandTest.java new file mode 100644 index 000000000..585f7448b --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaHKDFExpandTest.java @@ -0,0 +1,133 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.kdf; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyDerivationFunction; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyDerivation; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaHKDFExpandTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/kdf/PycaHKDFExpandTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(Algorithm.class); + assertThat(value0.asString()).isEqualTo("SHA256"); + + DetectionStore store_1 = + getStoreOfValueType(KeySize.class, detectionStore.getChildren()); + assertThat(store_1.getDetectionValues()).hasSize(1); + assertThat(store_1.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0_1 = store_1.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(KeySize.class); + assertThat(value0_1.asString()).isEqualTo("256"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // KeyDerivationFunction + INode keyDerivationFunctionNode = nodes.get(0); + assertThat(keyDerivationFunctionNode.getKind()).isEqualTo(KeyDerivationFunction.class); + assertThat(keyDerivationFunctionNode.getChildren()).hasSize(3); + assertThat(keyDerivationFunctionNode.asString()).isEqualTo("HKDF-SHA-256"); + + // KeyDerivation under KeyDerivationFunction + INode keyDerivationNode = keyDerivationFunctionNode.getChildren().get(KeyDerivation.class); + assertThat(keyDerivationNode).isNotNull(); + assertThat(keyDerivationNode.getChildren()).isEmpty(); + assertThat(keyDerivationNode.asString()).isEqualTo("KEYDERIVATION"); + + // KeyLength under KeyDerivationFunction + INode keyLengthNode = keyDerivationFunctionNode.getChildren().get(KeyLength.class); + assertThat(keyLengthNode).isNotNull(); + assertThat(keyLengthNode.getChildren()).isEmpty(); + assertThat(keyLengthNode.asString()).isEqualTo("256"); + + // MessageDigest under KeyDerivationFunction + INode messageDigestNode = keyDerivationFunctionNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode).isNotNull(); + assertThat(messageDigestNode.getChildren()).hasSize(4); + assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); + + // BlockSize under MessageDigest under KeyDerivationFunction + INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("512"); + + // Digest under MessageDigest under KeyDerivationFunction + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // DigestSize under MessageDigest under KeyDerivationFunction + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("256"); + + // Oid under MessageDigest under KeyDerivationFunction + INode oidNode = messageDigestNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaHKDFTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaHKDFTest.java new file mode 100644 index 000000000..1db1ba235 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaHKDFTest.java @@ -0,0 +1,133 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.kdf; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyDerivationFunction; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyDerivation; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaHKDFTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/kdf/PycaHKDFTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(Algorithm.class); + assertThat(value0.asString()).isEqualTo("SHA256"); + + DetectionStore store_1 = + getStoreOfValueType(KeySize.class, detectionStore.getChildren()); + assertThat(store_1.getDetectionValues()).hasSize(1); + assertThat(store_1.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0_1 = store_1.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(KeySize.class); + assertThat(value0_1.asString()).isEqualTo("256"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // KeyDerivationFunction + INode keyDerivationFunctionNode = nodes.get(0); + assertThat(keyDerivationFunctionNode.getKind()).isEqualTo(KeyDerivationFunction.class); + assertThat(keyDerivationFunctionNode.getChildren()).hasSize(3); + assertThat(keyDerivationFunctionNode.asString()).isEqualTo("HKDF-SHA-256"); + + // MessageDigest under KeyDerivationFunction + INode messageDigestNode = keyDerivationFunctionNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode).isNotNull(); + assertThat(messageDigestNode.getChildren()).hasSize(4); + assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); + + // DigestSize under MessageDigest under KeyDerivationFunction + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("256"); + + // Oid under MessageDigest under KeyDerivationFunction + INode oidNode = messageDigestNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + + // BlockSize under MessageDigest under KeyDerivationFunction + INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("512"); + + // Digest under MessageDigest under KeyDerivationFunction + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // KeyLength under KeyDerivationFunction + INode keyLengthNode = keyDerivationFunctionNode.getChildren().get(KeyLength.class); + assertThat(keyLengthNode).isNotNull(); + assertThat(keyLengthNode.getChildren()).isEmpty(); + assertThat(keyLengthNode.asString()).isEqualTo("256"); + + // KeyDerivation under KeyDerivationFunction + INode keyDerivationNode = keyDerivationFunctionNode.getChildren().get(KeyDerivation.class); + assertThat(keyDerivationNode).isNotNull(); + assertThat(keyDerivationNode.getChildren()).isEmpty(); + assertThat(keyDerivationNode.asString()).isEqualTo("KEYDERIVATION"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaKBKDFCMACTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaKBKDFCMACTest.java new file mode 100644 index 000000000..16e7df570 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaKBKDFCMACTest.java @@ -0,0 +1,142 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.kdf; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.Mac; +import com.ibm.mapper.model.Mode; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.KeyDerivation; +import com.ibm.mapper.model.functionality.Tag; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaKBKDFCMACTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/kdf/PycaKBKDFCMACTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(Algorithm.class); + assertThat(value0.asString()).isEqualTo("AES"); + + DetectionStore store_1 = + getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); + assertThat(store_1.getDetectionValues()).hasSize(1); + assertThat(store_1.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0_1 = store_1.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(com.ibm.engine.model.Mode.class); + assertThat(value0_1.asString()).isEqualTo("CounterMode"); + + DetectionStore store_2 = + getStoreOfValueType(KeySize.class, detectionStore.getChildren()); + assertThat(store_2.getDetectionValues()).hasSize(1); + assertThat(store_2.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0_2 = store_2.getDetectionValues().get(0); + assertThat(value0_2).isInstanceOf(KeySize.class); + assertThat(value0_2.asString()).isEqualTo("256"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // Mac + INode macNode = nodes.get(0); + assertThat(macNode.getKind()).isEqualTo(Mac.class); + assertThat(macNode.getChildren()).hasSize(4); + assertThat(macNode.asString()).isEqualTo("CMAC-AES"); + + // Tag under Mac + INode tagNode = macNode.getChildren().get(Tag.class); + assertThat(tagNode).isNotNull(); + assertThat(tagNode.getChildren()).isEmpty(); + assertThat(tagNode.asString()).isEqualTo("TAG"); + + // KeyDerivation under Mac + INode keyDerivationNode = macNode.getChildren().get(KeyDerivation.class); + assertThat(keyDerivationNode).isNotNull(); + assertThat(keyDerivationNode.getChildren()).isEmpty(); + assertThat(keyDerivationNode.asString()).isEqualTo("KEYDERIVATION"); + + // BlockCipher under Mac + INode blockCipherNode = macNode.getChildren().get(BlockCipher.class); + assertThat(blockCipherNode).isNotNull(); + assertThat(blockCipherNode.getChildren()).hasSize(3); + assertThat(blockCipherNode.asString()).isEqualTo("AES-CTR"); + + // BlockSize under BlockCipher under Mac + INode blockSizeNode = blockCipherNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("128"); + + // Mode under BlockCipher under Mac + INode modeNode = blockCipherNode.getChildren().get(Mode.class); + assertThat(modeNode).isNotNull(); + assertThat(modeNode.getChildren()).isEmpty(); + assertThat(modeNode.asString()).isEqualTo("CTR"); + + // Oid under BlockCipher under Mac + INode oidNode = blockCipherNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.1"); + + // KeyLength under Mac + INode keyLengthNode = macNode.getChildren().get(KeyLength.class); + assertThat(keyLengthNode).isNotNull(); + assertThat(keyLengthNode.getChildren()).isEmpty(); + assertThat(keyLengthNode.asString()).isEqualTo("256"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaKBKDFHMACTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaKBKDFHMACTest.java new file mode 100644 index 000000000..828cbf21d --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaKBKDFHMACTest.java @@ -0,0 +1,163 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.kdf; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.Mac; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Mode; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyDerivation; +import com.ibm.mapper.model.functionality.Tag; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaKBKDFHMACTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/kdf/PycaKBKDFHMACTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(Algorithm.class); + assertThat(value0.asString()).isEqualTo("SHA256"); + + DetectionStore store_1 = + getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); + assertThat(store_1.getDetectionValues()).hasSize(1); + assertThat(store_1.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0_1 = store_1.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(com.ibm.engine.model.Mode.class); + assertThat(value0_1.asString()).isEqualTo("CounterMode"); + + DetectionStore store_2 = + getStoreOfValueType(KeySize.class, detectionStore.getChildren()); + assertThat(store_2.getDetectionValues()).hasSize(1); + assertThat(store_2.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0_2 = store_2.getDetectionValues().get(0); + assertThat(value0_2).isInstanceOf(KeySize.class); + assertThat(value0_2.asString()).isEqualTo("256"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // Mac + INode macNode = nodes.get(0); + assertThat(macNode.getKind()).isEqualTo(Mac.class); + assertThat(macNode.getChildren()).hasSize(5); + assertThat(macNode.asString()).isEqualTo("HMAC-SHA-256"); + + // Tag under Mac + INode tagNode = macNode.getChildren().get(Tag.class); + assertThat(tagNode).isNotNull(); + assertThat(tagNode.getChildren()).isEmpty(); + assertThat(tagNode.asString()).isEqualTo("TAG"); + + // Oid under Mac + INode oidNode = macNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("1.2.840.113549.2.9"); + + // KeyLength under Mac + INode keyLengthNode = macNode.getChildren().get(KeyLength.class); + assertThat(keyLengthNode).isNotNull(); + assertThat(keyLengthNode.getChildren()).isEmpty(); + assertThat(keyLengthNode.asString()).isEqualTo("256"); + + // KeyDerivation under Mac + INode keyDerivationNode = macNode.getChildren().get(KeyDerivation.class); + assertThat(keyDerivationNode).isNotNull(); + assertThat(keyDerivationNode.getChildren()).isEmpty(); + assertThat(keyDerivationNode.asString()).isEqualTo("KEYDERIVATION"); + + // MessageDigest under Mac + INode messageDigestNode = macNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode).isNotNull(); + assertThat(messageDigestNode.getChildren()).hasSize(5); + assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); + + // Mode under MessageDigest under Mac + INode modeNode = messageDigestNode.getChildren().get(Mode.class); + assertThat(modeNode).isNotNull(); + assertThat(modeNode.getChildren()).isEmpty(); + assertThat(modeNode.asString()).isEqualTo("CTR"); + + // DigestSize under MessageDigest under Mac + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("256"); + + // Oid under MessageDigest under Mac + INode oidNode1 = messageDigestNode.getChildren().get(Oid.class); + assertThat(oidNode1).isNotNull(); + assertThat(oidNode1.getChildren()).isEmpty(); + assertThat(oidNode1.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + + // Digest under MessageDigest under Mac + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // BlockSize under MessageDigest under Mac + INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("512"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaPBKDF2Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaPBKDF2Test.java new file mode 100644 index 000000000..a714802e5 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaPBKDF2Test.java @@ -0,0 +1,147 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.kdf; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.AlgorithmParameter; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PasswordBasedKeyDerivationFunction; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyDerivation; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaPBKDF2Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/kdf/PycaPBKDF2TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(Algorithm.class); + assertThat(value0.asString()).isEqualTo("SHA256"); + + DetectionStore store_1 = + getStoreOfValueType(KeySize.class, detectionStore.getChildren()); + assertThat(store_1.getDetectionValues()).hasSize(1); + assertThat(store_1.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0_1 = store_1.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(KeySize.class); + assertThat(value0_1.asString()).isEqualTo("256"); + + DetectionStore store_2 = + getStoreOfValueType(AlgorithmParameter.class, detectionStore.getChildren()); + assertThat(store_2.getDetectionValues()).hasSize(1); + assertThat(store_2.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0_2 = store_2.getDetectionValues().get(0); + assertThat(value0_2).isInstanceOf(AlgorithmParameter.class); + assertThat(value0_2.asString()).isEqualTo("480000"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // PasswordBasedKeyDerivationFunction + INode passwordBasedKeyDerivationFunctionNode = nodes.get(0); + assertThat(passwordBasedKeyDerivationFunctionNode.getKind()) + .isEqualTo(PasswordBasedKeyDerivationFunction.class); + assertThat(passwordBasedKeyDerivationFunctionNode.getChildren()).hasSize(3); + assertThat(passwordBasedKeyDerivationFunctionNode.asString()).isEqualTo("PBKDF2-SHA-256"); + + // KeyDerivation under PasswordBasedKeyDerivationFunction + INode keyDerivationNode = + passwordBasedKeyDerivationFunctionNode.getChildren().get(KeyDerivation.class); + assertThat(keyDerivationNode).isNotNull(); + assertThat(keyDerivationNode.getChildren()).isEmpty(); + assertThat(keyDerivationNode.asString()).isEqualTo("KEYDERIVATION"); + + // MessageDigest under PasswordBasedKeyDerivationFunction + INode messageDigestNode = + passwordBasedKeyDerivationFunctionNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode).isNotNull(); + assertThat(messageDigestNode.getChildren()).hasSize(4); + assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); + + // Digest under MessageDigest under PasswordBasedKeyDerivationFunction + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // BlockSize under MessageDigest under PasswordBasedKeyDerivationFunction + INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("512"); + + // DigestSize under MessageDigest under PasswordBasedKeyDerivationFunction + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("256"); + + // Oid under MessageDigest under PasswordBasedKeyDerivationFunction + INode oidNode = messageDigestNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + + // KeyLength under PasswordBasedKeyDerivationFunction + INode keyLengthNode = + passwordBasedKeyDerivationFunctionNode.getChildren().get(KeyLength.class); + assertThat(keyLengthNode).isNotNull(); + assertThat(keyLengthNode.getChildren()).isEmpty(); + assertThat(keyLengthNode.asString()).isEqualTo("256"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaScryptTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaScryptTest.java new file mode 100644 index 000000000..f1a85a3fb --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaScryptTest.java @@ -0,0 +1,101 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.kdf; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.PasswordBasedKeyDerivationFunction; +import com.ibm.mapper.model.functionality.KeyDerivation; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaScryptTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/kdf/PycaScryptTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(ValueAction.class); + assertThat(value0.asString()).isEqualTo("Scrypt"); + + DetectionStore store_1 = + getStoreOfValueType(KeySize.class, detectionStore.getChildren()); + assertThat(store_1.getDetectionValues()).hasSize(1); + assertThat(store_1.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0_1 = store_1.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(KeySize.class); + assertThat(value0_1.asString()).isEqualTo("256"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // PasswordBasedKeyDerivationFunction + INode passwordBasedKeyDerivationFunctionNode = nodes.get(0); + assertThat(passwordBasedKeyDerivationFunctionNode.getKind()) + .isEqualTo(PasswordBasedKeyDerivationFunction.class); + assertThat(passwordBasedKeyDerivationFunctionNode.getChildren()).hasSize(2); + assertThat(passwordBasedKeyDerivationFunctionNode.asString()).isEqualTo("scrypt"); + + // KeyDerivation under PasswordBasedKeyDerivationFunction + INode keyDerivationNode = + passwordBasedKeyDerivationFunctionNode.getChildren().get(KeyDerivation.class); + assertThat(keyDerivationNode).isNotNull(); + assertThat(keyDerivationNode.getChildren()).isEmpty(); + assertThat(keyDerivationNode.asString()).isEqualTo("KEYDERIVATION"); + + // KeyLength under PasswordBasedKeyDerivationFunction + INode keyLengthNode = + passwordBasedKeyDerivationFunctionNode.getChildren().get(KeyLength.class); + assertThat(keyLengthNode).isNotNull(); + assertThat(keyLengthNode.getChildren()).isEmpty(); + assertThat(keyLengthNode.asString()).isEqualTo("256"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaX963KDFTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaX963KDFTest.java new file mode 100644 index 000000000..9310d6380 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaX963KDFTest.java @@ -0,0 +1,133 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.kdf; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyDerivationFunction; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyDerivation; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaX963KDFTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/kdf/PycaX963KDFTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(Algorithm.class); + assertThat(value0.asString()).isEqualTo("SHA256"); + + DetectionStore store_1 = + getStoreOfValueType(KeySize.class, detectionStore.getChildren()); + assertThat(store_1.getDetectionValues()).hasSize(1); + assertThat(store_1.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value0_1 = store_1.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(KeySize.class); + assertThat(value0_1.asString()).isEqualTo("256"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // KeyDerivationFunction + INode keyDerivationFunctionNode = nodes.get(0); + assertThat(keyDerivationFunctionNode.getKind()).isEqualTo(KeyDerivationFunction.class); + assertThat(keyDerivationFunctionNode.getChildren()).hasSize(3); + assertThat(keyDerivationFunctionNode.asString()).isEqualTo("ANSI-KDF-X9.63"); + + // KeyDerivation under KeyDerivationFunction + INode keyDerivationNode = keyDerivationFunctionNode.getChildren().get(KeyDerivation.class); + assertThat(keyDerivationNode).isNotNull(); + assertThat(keyDerivationNode.getChildren()).isEmpty(); + assertThat(keyDerivationNode.asString()).isEqualTo("KEYDERIVATION"); + + // KeyLength under KeyDerivationFunction + INode keyLengthNode = keyDerivationFunctionNode.getChildren().get(KeyLength.class); + assertThat(keyLengthNode).isNotNull(); + assertThat(keyLengthNode.getChildren()).isEmpty(); + assertThat(keyLengthNode.asString()).isEqualTo("256"); + + // MessageDigest under KeyDerivationFunction + INode messageDigestNode = keyDerivationFunctionNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode).isNotNull(); + assertThat(messageDigestNode.getChildren()).hasSize(4); + assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); + + // BlockSize under MessageDigest under KeyDerivationFunction + INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("512"); + + // Oid under MessageDigest under KeyDerivationFunction + INode oidNode = messageDigestNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + + // DigestSize under MessageDigest under KeyDerivationFunction + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("256"); + + // Digest under MessageDigest under KeyDerivationFunction + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/keyagreement/PycaKeyAgreementTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/keyagreement/PycaKeyAgreementTest.java new file mode 100644 index 000000000..9327a162f --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/keyagreement/PycaKeyAgreementTest.java @@ -0,0 +1,179 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.keyagreement; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.context.KeyAgreementContext; +import com.ibm.mapper.model.EllipticCurve; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyAgreement; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaKeyAgreementTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/keyagreement/PycaKeyAgreementTestFile.py", + this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + + if (findingId == 0) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyAgreementContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(KeyAction.class); + assertThat(value0.asString()).isEqualTo("GENERATION"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // KeyAgreement + INode keyAgreementNode = nodes.get(0); + assertThat(keyAgreementNode.getKind()).isEqualTo(KeyAgreement.class); + assertThat(keyAgreementNode.getChildren()).hasSize(3); + assertThat(keyAgreementNode.asString()).isEqualTo("x25519"); + + // EllipticCurve under KeyAgreement + INode ellipticCurveNode = keyAgreementNode.getChildren().get(EllipticCurve.class); + assertThat(ellipticCurveNode).isNotNull(); + assertThat(ellipticCurveNode.getChildren()).isEmpty(); + assertThat(ellipticCurveNode.asString()).isEqualTo("Curve25519"); + + // KeyGeneration under KeyAgreement + INode keyGenerationNode = keyAgreementNode.getChildren().get(KeyGeneration.class); + assertThat(keyGenerationNode).isNotNull(); + assertThat(keyGenerationNode.getChildren()).isEmpty(); + assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); + + // Oid under KeyAgreement + INode oidNode1 = keyAgreementNode.getChildren().get(Oid.class); + assertThat(oidNode1).isNotNull(); + assertThat(oidNode1.getChildren()).isEmpty(); + assertThat(oidNode1.asString()).isEqualTo("1.3.101.110"); + + } else if (findingId == 1) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyAgreementContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(KeyAction.class); + assertThat(value0.asString()).isEqualTo("GENERATION"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // KeyAgreement + INode keyAgreementNode = nodes.get(0); + assertThat(keyAgreementNode.getKind()).isEqualTo(KeyAgreement.class); + assertThat(keyAgreementNode.getChildren()).hasSize(3); + assertThat(keyAgreementNode.asString()).isEqualTo("x25519"); + + // EllipticCurve under KeyAgreement + INode ellipticCurveNode = keyAgreementNode.getChildren().get(EllipticCurve.class); + assertThat(ellipticCurveNode).isNotNull(); + assertThat(ellipticCurveNode.getChildren()).isEmpty(); + assertThat(ellipticCurveNode.asString()).isEqualTo("Curve25519"); + + // KeyGeneration under KeyAgreement + INode keyGenerationNode = keyAgreementNode.getChildren().get(KeyGeneration.class); + assertThat(keyGenerationNode).isNotNull(); + assertThat(keyGenerationNode.getChildren()).isEmpty(); + assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); + + // Oid under KeyAgreement + INode oidNode1 = keyAgreementNode.getChildren().get(Oid.class); + assertThat(oidNode1).isNotNull(); + assertThat(oidNode1.getChildren()).isEmpty(); + assertThat(oidNode1.asString()).isEqualTo("1.3.101.110"); + + } else { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyAgreementContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(KeyAction.class); + assertThat(value0.asString()).isEqualTo("GENERATION"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // KeyAgreement + INode keyAgreementNode1 = nodes.get(0); + assertThat(keyAgreementNode1.getKind()).isEqualTo(KeyAgreement.class); + assertThat(keyAgreementNode1.getChildren()).hasSize(3); + assertThat(keyAgreementNode1.asString()).isEqualTo("x448"); + + // EllipticCurve under KeyAgreement + INode ellipticCurveNode1 = keyAgreementNode1.getChildren().get(EllipticCurve.class); + assertThat(ellipticCurveNode1).isNotNull(); + assertThat(ellipticCurveNode1.getChildren()).isEmpty(); + assertThat(ellipticCurveNode1.asString()).isEqualTo("Curve448"); + + // KeyGeneration under KeyAgreement + INode keyGenerationNode1 = keyAgreementNode1.getChildren().get(KeyGeneration.class); + assertThat(keyGenerationNode1).isNotNull(); + assertThat(keyGenerationNode1.getChildren()).isEmpty(); + assertThat(keyGenerationNode1.asString()).isEqualTo("KEYGENERATION"); + + // Oid under KeyAgreement + INode oidNode3 = keyAgreementNode1.getChildren().get(Oid.class); + assertThat(oidNode3).isNotNull(); + assertThat(oidNode3.getChildren()).isEmpty(); + assertThat(oidNode3.asString()).isEqualTo("1.3.101.111"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaCMACTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaCMACTest.java new file mode 100644 index 000000000..afeab14a3 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaCMACTest.java @@ -0,0 +1,101 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.mac; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.context.MacContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mac; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Tag; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaCMACTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/mac/PycaCMACTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(MacContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(Algorithm.class); + assertThat(value0.asString()).isEqualTo("AES"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // Mac + INode macNode = nodes.get(0); + assertThat(macNode.getKind()).isEqualTo(Mac.class); + assertThat(macNode.getChildren()).hasSize(2); + assertThat(macNode.asString()).isEqualTo("CMAC-AES"); + + // BlockCipher under Mac + INode blockCipherNode = macNode.getChildren().get(BlockCipher.class); + assertThat(blockCipherNode).isNotNull(); + assertThat(blockCipherNode.getChildren()).hasSize(2); + assertThat(blockCipherNode.asString()).isEqualTo("AES"); + + // BlockSize under BlockCipher under Mac + INode blockSizeNode = blockCipherNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("128"); + + // Oid under BlockCipher under Mac + INode oidNode = blockCipherNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.1"); + + // Tag under Mac + INode tagNode = macNode.getChildren().get(Tag.class); + assertThat(tagNode).isNotNull(); + assertThat(tagNode.getChildren()).isEmpty(); + assertThat(tagNode.asString()).isEqualTo("TAG"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaHMACTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaHMACTest.java new file mode 100644 index 000000000..374ee293d --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaHMACTest.java @@ -0,0 +1,122 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.mac; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.context.MacContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mac; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.Tag; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaHMACTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/mac/PycaHMACTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(MacContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(Algorithm.class); + assertThat(value0.asString()).isEqualTo("SHA256"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // Mac + INode macNode = nodes.get(0); + assertThat(macNode.getKind()).isEqualTo(Mac.class); + assertThat(macNode.getChildren()).hasSize(3); + assertThat(macNode.asString()).isEqualTo("HMAC-SHA-256"); + + // MessageDigest under Mac + INode messageDigestNode = macNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode).isNotNull(); + assertThat(messageDigestNode.getChildren()).hasSize(4); + assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); + + // Digest under MessageDigest under Mac + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // BlockSize under MessageDigest under Mac + INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("512"); + + // Oid under MessageDigest under Mac + INode oidNode = messageDigestNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + + // DigestSize under MessageDigest under Mac + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("256"); + + // Tag under Mac + INode tagNode = macNode.getChildren().get(Tag.class); + assertThat(tagNode).isNotNull(); + assertThat(tagNode.getChildren()).isEmpty(); + assertThat(tagNode.asString()).isEqualTo("TAG"); + + // Oid under Mac + INode oidNode1 = macNode.getChildren().get(Oid.class); + assertThat(oidNode1).isNotNull(); + assertThat(oidNode1.getChildren()).isEmpty(); + assertThat(oidNode1.asString()).isEqualTo("1.2.840.113549.2.9"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaMacDetectionInCustomFunctionTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaMacDetectionInCustomFunctionTest.java new file mode 100644 index 000000000..e7806b573 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaMacDetectionInCustomFunctionTest.java @@ -0,0 +1,131 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.mac; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.context.MacContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mac; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.Tag; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +/** + * Verifies that cryptographic operations inside user-defined functions are detected during standard + * AST traversal. This test covers both positive cases (cryptographic operations) and negative cases + * (non-cryptographic functions). + */ +class PycaMacDetectionInCustomFunctionTest extends TestBase { + + @Test + void testCryptographicOperationInCustomFunction() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/mac/PycaMacDetectionInCustomFunctionTestFile.py", + this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + + // Verifies that cryptographic operations inside user-defined functions are detected + // during standard AST traversal. + + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(MacContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(Algorithm.class); + assertThat(value0.asString()).isEqualTo("SHA256"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // Mac + INode macNode = nodes.get(0); + assertThat(macNode.getKind()).isEqualTo(Mac.class); + assertThat(macNode.getChildren()).hasSize(3); + assertThat(macNode.asString()).isEqualTo("HMAC-SHA-256"); + + // MessageDigest under Mac + INode messageDigestNode = macNode.getChildren().get(MessageDigest.class); + assertThat(messageDigestNode).isNotNull(); + assertThat(messageDigestNode.getChildren()).hasSize(4); + assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); + + // Digest under MessageDigest under Mac + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // BlockSize under MessageDigest under Mac + INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("512"); + + // Oid under MessageDigest under Mac + INode oidNode = messageDigestNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + + // DigestSize under MessageDigest under Mac + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("256"); + + // Tag under Mac + INode tagNode = macNode.getChildren().get(Tag.class); + assertThat(tagNode).isNotNull(); + assertThat(tagNode.getChildren()).isEmpty(); + assertThat(tagNode.asString()).isEqualTo("TAG"); + + // Oid under Mac + INode oidNode1 = macNode.getChildren().get(Oid.class); + assertThat(oidNode1).isNotNull(); + assertThat(oidNode1.getChildren()).isEmpty(); + assertThat(oidNode1.asString()).isEqualTo("1.2.840.113549.2.9"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaPoly1305Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaPoly1305Test.java new file mode 100644 index 000000000..b6e9d0d96 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaPoly1305Test.java @@ -0,0 +1,80 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.mac; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.MacContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mac; +import com.ibm.mapper.model.functionality.Tag; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaPoly1305Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/mac/PycaPoly1305TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(MacContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(ValueAction.class); + assertThat(value0.asString()).isEqualTo("Poly1305"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // Mac + INode macNode = nodes.get(0); + assertThat(macNode.getKind()).isEqualTo(Mac.class); + assertThat(macNode.getChildren()).hasSize(1); + assertThat(macNode.asString()).isEqualTo("Poly1305"); + + // Tag under Mac + INode tagNode = macNode.getChildren().get(Tag.class); + assertThat(tagNode).isNotNull(); + assertThat(tagNode.getChildren()).isEmpty(); + assertThat(tagNode.asString()).isEqualTo("TAG"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/padding/PycaPaddingTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/padding/PycaPaddingTest.java new file mode 100644 index 000000000..7d3a43556 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/padding/PycaPaddingTest.java @@ -0,0 +1,125 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.padding; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mode; +import com.ibm.mapper.model.Padding; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaPaddingTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/padding/PycaPaddingTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(Algorithm.class); + assertThat(value0.asString()).isEqualTo("CAST5"); + + DetectionStore store_1 = + getStoreOfValueType(ValueAction.class, detectionStore.getChildren()); + assertThat(store_1.getDetectionValues()).hasSize(1); + assertThat(store_1.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0_1 = store_1.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(ValueAction.class); + assertThat(value0_1.asString()).isEqualTo("ANSIX923"); + + DetectionStore store_1_1 = + getStoreOfValueType(com.ibm.engine.model.BlockSize.class, store_1.getChildren()); + assertThat(store_1_1.getDetectionValues()).hasSize(1); + assertThat(store_1_1.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0_1_1 = store_1_1.getDetectionValues().get(0); + assertThat(value0_1_1).isInstanceOf(com.ibm.engine.model.BlockSize.class); + assertThat(value0_1_1.asString()).isEqualTo("128"); + + DetectionStore store_2 = + getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); + assertThat(store_2.getDetectionValues()).hasSize(1); + assertThat(store_2.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0_2 = store_2.getDetectionValues().get(0); + assertThat(value0_2).isInstanceOf(com.ibm.engine.model.Mode.class); + assertThat(value0_2.asString()).isEqualTo("CFB"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // BlockCipher + INode blockCipherNode = nodes.get(0); + assertThat(blockCipherNode.getKind()).isEqualTo(BlockCipher.class); + assertThat(blockCipherNode.getChildren()).hasSize(3); + assertThat(blockCipherNode.asString()).isEqualTo("CAST5-CFB"); + + // BlockSize under BlockCipher + INode blockSizeNode = blockCipherNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("64"); + + // Mode under BlockCipher + INode modeNode = blockCipherNode.getChildren().get(Mode.class); + assertThat(modeNode).isNotNull(); + assertThat(modeNode.getChildren()).isEmpty(); + assertThat(modeNode.asString()).isEqualTo("CFB"); + + // Padding under BlockCipher + INode paddingNode = blockCipherNode.getChildren().get(Padding.class); + assertThat(paddingNode).isNotNull(); + assertThat(paddingNode.getChildren()).hasSize(1); + assertThat(paddingNode.asString()).isEqualTo("ANSI X9.23"); + + // BlockSize under Padding under BlockCipher + INode blockSizeNode1 = paddingNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode1).isNotNull(); + assertThat(blockSizeNode1.getChildren()).isEmpty(); + assertThat(blockSizeNode1.asString()).isEqualTo("128"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher1Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher1Test.java new file mode 100644 index 000000000..36834055c --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher1Test.java @@ -0,0 +1,161 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.symmetric; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mode; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.Padding; +import com.ibm.mapper.model.functionality.Decrypt; +import com.ibm.mapper.model.functionality.Encrypt; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaCipher1Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/symmetric/PycaCipher1TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(Algorithm.class); + assertThat(value0.asString()).isEqualTo("AES"); + + List> store_1 = + getStoresOfValueType(CipherAction.class, detectionStore.getChildren()); + for (DetectionStore store : store_1) { + assertThat(store.getDetectionValues()).hasSize(1); + assertThat(store.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0_1 = store.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(CipherAction.class); + assertThat(value0_1.asString()) + .satisfiesAnyOf( + s -> assertThat(s).isEqualTo("DECRYPT"), + s -> assertThat(s).isEqualTo("ENCRYPT")); + } + + DetectionStore store_3 = + getStoreOfValueType(ValueAction.class, detectionStore.getChildren()); + assertThat(store_3.getDetectionValues()).hasSize(1); + assertThat(store_3.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0_3 = store_3.getDetectionValues().get(0); + assertThat(value0_3).isInstanceOf(ValueAction.class); + assertThat(value0_3.asString()).isEqualTo("PKCS7"); + + DetectionStore store_3_1 = + getStoreOfValueType(com.ibm.engine.model.BlockSize.class, store_3.getChildren()); + assertThat(store_3_1.getDetectionValues()).hasSize(1); + assertThat(store_3_1.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0_3_1 = store_3_1.getDetectionValues().get(0); + assertThat(value0_3_1).isInstanceOf(com.ibm.engine.model.BlockSize.class); + assertThat(value0_3_1.asString()).isEqualTo("80"); + + DetectionStore store_4 = + getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); + assertThat(store_4.getDetectionValues()).hasSize(1); + assertThat(store_4.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0_4 = store_4.getDetectionValues().get(0); + assertThat(value0_4).isInstanceOf(com.ibm.engine.model.Mode.class); + assertThat(value0_4.asString()).isEqualTo("CBC"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // BlockCipher + INode blockCipherNode = nodes.get(0); + assertThat(blockCipherNode.getKind()).isEqualTo(BlockCipher.class); + assertThat(blockCipherNode.getChildren()).hasSize(6); + assertThat(blockCipherNode.asString()).isEqualTo("AES-CBC-PKCS7"); + + // Mode under BlockCipher + INode modeNode = blockCipherNode.getChildren().get(Mode.class); + assertThat(modeNode).isNotNull(); + assertThat(modeNode.getChildren()).isEmpty(); + assertThat(modeNode.asString()).isEqualTo("CBC"); + + // Decrypt under BlockCipher + INode decryptNode = blockCipherNode.getChildren().get(Decrypt.class); + assertThat(decryptNode).isNotNull(); + assertThat(decryptNode.getChildren()).isEmpty(); + assertThat(decryptNode.asString()).isEqualTo("DECRYPT"); + + // Oid under BlockCipher + INode oidNode = blockCipherNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.1"); + + // Encrypt under BlockCipher + INode encryptNode = blockCipherNode.getChildren().get(Encrypt.class); + assertThat(encryptNode).isNotNull(); + assertThat(encryptNode.getChildren()).isEmpty(); + assertThat(encryptNode.asString()).isEqualTo("ENCRYPT"); + + // Padding under BlockCipher + INode paddingNode = blockCipherNode.getChildren().get(Padding.class); + assertThat(paddingNode).isNotNull(); + assertThat(paddingNode.getChildren()).hasSize(1); + assertThat(paddingNode.asString()).isEqualTo("PKCS7"); + + // BlockSize under Padding under BlockCipher + INode blockSizeNode = paddingNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("80"); + + // BlockSize under BlockCipher + INode blockSizeNode1 = blockCipherNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode1).isNotNull(); + assertThat(blockSizeNode1.getChildren()).isEmpty(); + assertThat(blockSizeNode1.asString()).isEqualTo("128"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher2Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher2Test.java new file mode 100644 index 000000000..a69bca299 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher2Test.java @@ -0,0 +1,104 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.symmetric; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mode; +import com.ibm.mapper.model.functionality.Encrypt; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaCipher2Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/symmetric/PycaCipher2TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(Algorithm.class); + assertThat(value0.asString()).isEqualTo("Camellia"); + + DetectionStore store_1 = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + assertThat(store_1.getDetectionValues()).hasSize(1); + assertThat(store_1.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0_1 = store_1.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(CipherAction.class); + assertThat(value0_1.asString()).isEqualTo("ENCRYPT"); + + DetectionStore store_2 = + getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); + assertThat(store_2.getDetectionValues()).hasSize(1); + assertThat(store_2.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0_2 = store_2.getDetectionValues().get(0); + assertThat(value0_2).isInstanceOf(com.ibm.engine.model.Mode.class); + assertThat(value0_2.asString()).isEqualTo("OFB"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // BlockCipher + INode blockCipherNode = nodes.get(0); + assertThat(blockCipherNode.getKind()).isEqualTo(BlockCipher.class); + assertThat(blockCipherNode.getChildren()).hasSize(2); + assertThat(blockCipherNode.asString()).isEqualTo("CAMELLIA-OFB"); + + // Encrypt under BlockCipher + INode encryptNode = blockCipherNode.getChildren().get(Encrypt.class); + assertThat(encryptNode).isNotNull(); + assertThat(encryptNode.getChildren()).isEmpty(); + assertThat(encryptNode.asString()).isEqualTo("ENCRYPT"); + + // Mode under BlockCipher + INode modeNode = blockCipherNode.getChildren().get(Mode.class); + assertThat(modeNode).isNotNull(); + assertThat(modeNode.getChildren()).isEmpty(); + assertThat(modeNode.asString()).isEqualTo("OFB"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher3Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher3Test.java new file mode 100644 index 000000000..0ad33a939 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher3Test.java @@ -0,0 +1,115 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.symmetric; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mode; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Encrypt; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaCipher3Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/symmetric/PycaCipher3TestFile.py", this); + } + + @SuppressWarnings("null") + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(Algorithm.class); + assertThat(value0.asString()).isEqualTo("AES"); + + List> store_1 = + getStoresOfValueType(CipherAction.class, detectionStore.getChildren()); + assertThat(store_1).isNotNull(); + for (DetectionStore store : store_1) { + assertThat(store.getDetectionValues()).hasSize(1); + assertThat(store.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0_1 = store.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(CipherAction.class); + assertThat(value0_1.asString()).isEqualTo("ENCRYPT"); + } + + // /* + // * Translation + // */ + assertThat(nodes).hasSize(1); + + // BlockCipher + INode blockCipherNode = nodes.get(0); + assertThat(blockCipherNode.getKind()).isEqualTo(BlockCipher.class); + assertThat(blockCipherNode.getChildren()).hasSize(4); + assertThat(blockCipherNode.asString()).isEqualTo("AES-CBC"); + + // Mode under BlockCipher + INode modeNode = blockCipherNode.getChildren().get(Mode.class); + assertThat(modeNode).isNotNull(); + assertThat(modeNode.getChildren()).isEmpty(); + assertThat(modeNode.asString()).isEqualTo("CBC"); + + // Oid under BlockCipher + INode oidNode = blockCipherNode.getChildren().get(Oid.class); + assertThat(oidNode).isNotNull(); + assertThat(oidNode.getChildren()).isEmpty(); + assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.1"); + + // Encrypt under BlockCipher + INode encryptNode = blockCipherNode.getChildren().get(Encrypt.class); + assertThat(encryptNode).isNotNull(); + assertThat(encryptNode.getChildren()).isEmpty(); + assertThat(encryptNode.asString()).isEqualTo("ENCRYPT"); + + // BlockSize under BlockCipher + INode blockSizeNode1 = blockCipherNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode1).isNotNull(); + assertThat(blockSizeNode1.getChildren()).isEmpty(); + assertThat(blockSizeNode1.asString()).isEqualTo("128"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaStreamCipher1Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaStreamCipher1Test.java new file mode 100644 index 000000000..98776e4f0 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaStreamCipher1Test.java @@ -0,0 +1,89 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.symmetric; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.StreamCipher; +import com.ibm.mapper.model.functionality.Encrypt; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaStreamCipher1Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/symmetric/PycaStreamCipher1TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(Algorithm.class); + assertThat(value0.asString()).isEqualTo("ChaCha20"); + + DetectionStore store_1 = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + assertThat(store_1.getDetectionValues()).hasSize(1); + assertThat(store_1.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0_1 = store_1.getDetectionValues().get(0); + assertThat(value0_1).isInstanceOf(CipherAction.class); + assertThat(value0_1.asString()).isEqualTo("ENCRYPT"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // StreamCipher + INode streamCipherNode = nodes.get(0); + assertThat(streamCipherNode.getKind()).isEqualTo(StreamCipher.class); + assertThat(streamCipherNode.getChildren()).hasSize(1); + assertThat(streamCipherNode.asString()).isEqualTo("ChaCha20"); + + // Encrypt under BlockCipher + INode encryptNode = streamCipherNode.getChildren().get(Encrypt.class); + assertThat(encryptNode).isNotNull(); + assertThat(encryptNode.getChildren()).isEmpty(); + assertThat(encryptNode.asString()).isEqualTo("ENCRYPT"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/wrapping/PycaWrappingTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/wrapping/PycaWrappingTest.java new file mode 100644 index 000000000..9c1a34897 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/wrapping/PycaWrappingTest.java @@ -0,0 +1,87 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.wrapping; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.KeyWrap; +import com.ibm.mapper.model.functionality.Encapsulate; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaWrappingTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/wrapping/PycaWrappingTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(CipherAction.class); + assertThat(value0.asString()).isEqualTo("WRAP"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // BlockCipher + INode blockCipherNode = nodes.get(0); + assertThat(blockCipherNode.getKind()).isEqualTo(KeyWrap.class); + assertThat(blockCipherNode.getChildren()).hasSize(4); + assertThat(blockCipherNode.asString()).isEqualTo("AES-128"); + + // KeyLength under BlockCipher + INode keyLengthNode = blockCipherNode.getChildren().get(KeyLength.class); + assertThat(keyLengthNode).isNotNull(); + assertThat(keyLengthNode.getChildren()).isEmpty(); + assertThat(keyLengthNode.asString()).isEqualTo("128"); + + // Encapsulate under BlockCipher + INode encapsulateNode = blockCipherNode.getChildren().get(Encapsulate.class); + assertThat(encapsulateNode).isNotNull(); + assertThat(encapsulateNode.getChildren()).isEmpty(); + assertThat(encapsulateNode.asString()).isEqualTo("ENCAPSULATE"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pyca/wrapping/PycaWrappingWithPaddingTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/wrapping/PycaWrappingWithPaddingTest.java new file mode 100644 index 000000000..bfeeec87f --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/wrapping/PycaWrappingWithPaddingTest.java @@ -0,0 +1,88 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pyca.wrapping; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.KeyWrap; +import com.ibm.mapper.model.functionality.Encapsulate; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +class PycaWrappingWithPaddingTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/wrapping/PycaWrappingWithPaddingTestFile.py", + this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(CipherAction.class); + assertThat(value0.asString()).isEqualTo("WRAP"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // BlockCipher + INode blockCipherNode = nodes.get(0); + assertThat(blockCipherNode.getKind()).isEqualTo(KeyWrap.class); + assertThat(blockCipherNode.getChildren()).hasSize(4); + assertThat(blockCipherNode.asString()).isEqualTo("AES-128"); + + // KeyLength under BlockCipher + INode keyLengthNode = blockCipherNode.getChildren().get(KeyLength.class); + assertThat(keyLengthNode).isNotNull(); + assertThat(keyLengthNode.getChildren()).isEmpty(); + assertThat(keyLengthNode.asString()).isEqualTo("128"); + + // Encapsulate under BlockCipher + INode encapsulateNode = blockCipherNode.getChildren().get(Encapsulate.class); + assertThat(encapsulateNode).isNotNull(); + assertThat(encapsulateNode.getChildren()).isEmpty(); + assertThat(encapsulateNode.asString()).isEqualTo("ENCAPSULATE"); + } +} From 90c9491610a17302839096c6e02c06549468affe Mon Sep 17 00:00:00 2001 From: san-zrl Date: Fri, 14 Aug 2026 12:38:45 +0200 Subject: [PATCH 09/13] python/pycrypto: add tests and fixtures for PyCryptodome detection rules Covers: cipher (AES, DES, 3DES, Blowfish, CAST5, RC2, RC4, ChaCha20, ChaCha20-Poly1305, Salsa20, PKCS1-OAEP, PKCS1-v1.5), hash (MD2/4/5, SHA-1/2/3, RIPEMD-160, BLAKE2b/s, TupleHash128, cSHAKE256), KDF (PBKDF1, PBKDF2, scrypt, HKDF), MAC (HMAC, CMAC), key agreement (DH, X25519/X448), public key (RSA, DSA, ECC, ElGamal), signature (PKCS1v15, PSS, DSS, ECDSA, EdDSA). Signed-off-by: san-zrl --- .../detection/pycrypto/cipher/AESTestFile.py | 5 + .../pycrypto/cipher/BlowfishTestFile.py | 4 + .../pycrypto/cipher/CAST5TestFile.py | 4 + .../cipher/ChaCha20Poly1305TestFile.py | 7 + .../pycrypto/cipher/ChaCha20TestFile.py | 5 + .../detection/pycrypto/cipher/DESTestFile.py | 5 + .../pycrypto/cipher/PKCS1OAEPTestFile.py | 6 + .../pycrypto/cipher/PKCS1v15TestFile.py | 5 + .../detection/pycrypto/cipher/RC2TestFile.py | 4 + .../detection/pycrypto/cipher/RC4TestFile.py | 4 + .../pycrypto/cipher/Salsa20TestFile.py | 5 + .../pycrypto/cipher/TripleDESTestFile.py | 4 + .../pycrypto/hash/BLAKE2bTestFile.py | 3 + .../pycrypto/hash/BLAKE2sTestFile.py | 3 + .../detection/pycrypto/hash/MD2TestFile.py | 3 + .../detection/pycrypto/hash/MD5TestFile.py | 3 + .../pycrypto/hash/RIPEMD160TestFile.py | 3 + .../detection/pycrypto/hash/SHA1TestFile.py | 3 + .../detection/pycrypto/hash/SHA224TestFile.py | 3 + .../detection/pycrypto/hash/SHA256TestFile.py | 3 + .../detection/pycrypto/hash/SHA384TestFile.py | 3 + .../pycrypto/hash/SHA3_224TestFile.py | 3 + .../pycrypto/hash/SHA3_256TestFile.py | 3 + .../pycrypto/hash/SHA3_384TestFile.py | 3 + .../pycrypto/hash/SHA3_512TestFile.py | 3 + .../detection/pycrypto/hash/SHA512TestFile.py | 3 + .../pycrypto/hash/TupleHash128TestFile.py | 3 + .../pycrypto/hash/cSHAKE256TestFile.py | 3 + .../detection/pycrypto/kdf/BcryptTestFile.py | 6 + .../detection/pycrypto/kdf/HKDFTestFile.py | 7 + .../detection/pycrypto/kdf/PBKDF1TestFile.py | 7 + .../detection/pycrypto/kdf/PBKDF2TestFile.py | 7 + .../pycrypto/kdf/SP800108CounterTestFile.py | 9 ++ .../detection/pycrypto/kdf/ScryptTestFile.py | 6 + .../pycrypto/keyagreement/ECDHTestFile.py | 14 ++ .../pycrypto/keyagreement/X25519TestFile.py | 14 ++ .../pycrypto/keyagreement/X448TestFile.py | 13 ++ .../detection/pycrypto/mac/CMACTestFile.py | 5 + .../detection/pycrypto/mac/HMACTestFile.py | 4 + .../detection/pycrypto/mac/KMAC128TestFile.py | 4 + .../detection/pycrypto/mac/KMAC256TestFile.py | 4 + .../pycrypto/mac/Poly1305TestFile.py | 4 + .../pycrypto/publickey/DSATestFile.py | 6 + .../pycrypto/publickey/ECCTestFile.py | 6 + .../pycrypto/publickey/ElGamalTestFile.py | 5 + .../pycrypto/publickey/RSATestFile.py | 6 + .../pycrypto/signature/DSSSignTestFile.py | 9 ++ .../pycrypto/signature/DSSVerifyTestFile.py | 15 ++ .../pycrypto/signature/ECDSASignTestFile.py | 9 ++ .../pycrypto/signature/ECDSAVerifyTestFile.py | 15 ++ .../pycrypto/signature/EdDSASignTestFile.py | 9 ++ .../pycrypto/signature/EdDSAVerifyTestFile.py | 14 ++ .../signature/PKCS1v15SignTestFile.py | 9 ++ .../signature/PKCS1v15VerifyTestFile.py | 15 ++ .../pycrypto/signature/PSSSignTestFile.py | 9 ++ .../pycrypto/signature/PSSVerifyTestFile.py | 16 ++ .../detection/pycrypto/cipher/AESTest.java | 106 +++++++++++++ .../pycrypto/cipher/BlowfishTest.java | 88 +++++++++++ .../detection/pycrypto/cipher/CAST5Test.java | 94 ++++++++++++ .../pycrypto/cipher/ChaCha20Poly1305Test.java | 79 ++++++++++ .../pycrypto/cipher/ChaCha20Test.java | 67 ++++++++ .../detection/pycrypto/cipher/DESTest.java | 100 ++++++++++++ .../pycrypto/cipher/PKCS1OAEPTest.java | 117 ++++++++++++++ .../pycrypto/cipher/PKCS1v15Test.java | 89 +++++++++++ .../detection/pycrypto/cipher/RC2Test.java | 88 +++++++++++ .../detection/pycrypto/cipher/RC4Test.java | 77 ++++++++++ .../pycrypto/cipher/Salsa20Test.java | 83 ++++++++++ .../pycrypto/cipher/TripleDESTest.java | 84 ++++++++++ .../detection/pycrypto/hash/BLAKE2bTest.java | 80 ++++++++++ .../detection/pycrypto/hash/BLAKE2sTest.java | 80 ++++++++++ .../detection/pycrypto/hash/MD2Test.java | 86 +++++++++++ .../detection/pycrypto/hash/MD5Test.java | 86 +++++++++++ .../pycrypto/hash/RIPEMD160Test.java | 80 ++++++++++ .../detection/pycrypto/hash/SHA1Test.java | 92 +++++++++++ .../detection/pycrypto/hash/SHA224Test.java | 92 +++++++++++ .../detection/pycrypto/hash/SHA256Test.java | 92 +++++++++++ .../detection/pycrypto/hash/SHA384Test.java | 92 +++++++++++ .../detection/pycrypto/hash/SHA3_224Test.java | 92 +++++++++++ .../detection/pycrypto/hash/SHA3_256Test.java | 92 +++++++++++ .../detection/pycrypto/hash/SHA3_384Test.java | 92 +++++++++++ .../detection/pycrypto/hash/SHA3_512Test.java | 92 +++++++++++ .../detection/pycrypto/hash/SHA512Test.java | 92 +++++++++++ .../pycrypto/hash/TupleHash128Test.java | 80 ++++++++++ .../pycrypto/hash/cSHAKE256Test.java | 80 ++++++++++ .../detection/pycrypto/kdf/HKDFTest.java | 102 ++++++++++++ .../detection/pycrypto/kdf/PBKDF1Test.java | 117 ++++++++++++++ .../detection/pycrypto/kdf/PBKDF2Test.java | 111 ++++++++++++++ .../pycrypto/kdf/SP800108CounterTest.java | 82 ++++++++++ .../detection/pycrypto/kdf/ScryptTest.java | 82 ++++++++++ .../pycrypto/keyagreement/ECDHTest.java | 82 ++++++++++ .../pycrypto/keyagreement/X25519Test.java | 87 +++++++++++ .../pycrypto/keyagreement/X448Test.java | 86 +++++++++++ .../detection/pycrypto/mac/CMACTest.java | 92 +++++++++++ .../detection/pycrypto/mac/HMACTest.java | 91 +++++++++++ .../detection/pycrypto/mac/KMAC128Test.java | 99 ++++++++++++ .../detection/pycrypto/mac/KMAC256Test.java | 99 ++++++++++++ .../detection/pycrypto/mac/Poly1305Test.java | 80 ++++++++++ .../detection/pycrypto/publickey/DSATest.java | 143 +++++++++++++++++ .../detection/pycrypto/publickey/ECCTest.java | 142 +++++++++++++++++ .../pycrypto/publickey/ElGamalTest.java | 100 ++++++++++++ .../detection/pycrypto/publickey/RSATest.java | 145 ++++++++++++++++++ .../pycrypto/signature/DSSSignTest.java | 128 ++++++++++++++++ .../pycrypto/signature/DSSVerifyTest.java | 128 ++++++++++++++++ .../pycrypto/signature/ECDSASignTest.java | 132 ++++++++++++++++ .../pycrypto/signature/ECDSAVerifyTest.java | 132 ++++++++++++++++ .../pycrypto/signature/EdDSASignTest.java | 127 +++++++++++++++ .../pycrypto/signature/EdDSAVerifyTest.java | 127 +++++++++++++++ .../pycrypto/signature/PKCS1v15SignTest.java | 141 +++++++++++++++++ .../signature/PKCS1v15VerifyTest.java | 142 +++++++++++++++++ .../pycrypto/signature/PSSSignTest.java | 135 ++++++++++++++++ .../pycrypto/signature/PSSVerifyTest.java | 135 ++++++++++++++++ 111 files changed, 5892 insertions(+) create mode 100644 python/src/test/files/rules/detection/pycrypto/cipher/AESTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/cipher/BlowfishTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/cipher/CAST5TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/cipher/ChaCha20Poly1305TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/cipher/ChaCha20TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/cipher/DESTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/cipher/PKCS1OAEPTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/cipher/PKCS1v15TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/cipher/RC2TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/cipher/RC4TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/cipher/Salsa20TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/cipher/TripleDESTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/hash/BLAKE2bTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/hash/BLAKE2sTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/hash/MD2TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/hash/MD5TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/hash/RIPEMD160TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/hash/SHA1TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/hash/SHA224TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/hash/SHA256TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/hash/SHA384TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/hash/SHA3_224TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/hash/SHA3_256TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/hash/SHA3_384TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/hash/SHA3_512TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/hash/SHA512TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/hash/TupleHash128TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/hash/cSHAKE256TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/kdf/BcryptTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/kdf/HKDFTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/kdf/PBKDF1TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/kdf/PBKDF2TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/kdf/SP800108CounterTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/kdf/ScryptTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/keyagreement/ECDHTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/keyagreement/X25519TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/keyagreement/X448TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/mac/CMACTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/mac/HMACTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/mac/KMAC128TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/mac/KMAC256TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/mac/Poly1305TestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/publickey/DSATestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/publickey/ECCTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/publickey/ElGamalTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/publickey/RSATestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/signature/DSSSignTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/signature/DSSVerifyTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/signature/ECDSASignTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/signature/ECDSAVerifyTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/signature/EdDSASignTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/signature/EdDSAVerifyTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/signature/PKCS1v15SignTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/signature/PKCS1v15VerifyTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/signature/PSSSignTestFile.py create mode 100644 python/src/test/files/rules/detection/pycrypto/signature/PSSVerifyTestFile.py create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/AESTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/BlowfishTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/CAST5Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/ChaCha20Poly1305Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/ChaCha20Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/DESTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/PKCS1OAEPTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/PKCS1v15Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/RC2Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/RC4Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/Salsa20Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/TripleDESTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/BLAKE2bTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/BLAKE2sTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/MD2Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/MD5Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/RIPEMD160Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA1Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA224Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA256Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA384Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_224Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_256Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_384Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_512Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA512Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/TupleHash128Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/cSHAKE256Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/HKDFTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/PBKDF1Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/PBKDF2Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/SP800108CounterTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/ScryptTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/ECDHTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/X25519Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/X448Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/CMACTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/HMACTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/KMAC128Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/KMAC256Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/Poly1305Test.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/DSATest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/ECCTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/ElGamalTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/RSATest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/DSSSignTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/DSSVerifyTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/ECDSASignTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/ECDSAVerifyTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/EdDSASignTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/EdDSAVerifyTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PKCS1v15SignTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PKCS1v15VerifyTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PSSSignTest.java create mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PSSVerifyTest.java diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/AESTestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/AESTestFile.py new file mode 100644 index 000000000..b24c6c530 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/AESTestFile.py @@ -0,0 +1,5 @@ +from Crypto.Cipher import AES + +key_aes = b"0123456789abcdef" +cipher_aes = AES.new(key_aes, AES.MODE_CBC, b'some init vector') # Noncompliant {{(BlockCipher) AES-CBC}} +cipher_aes.encrypt(b'some message') diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/BlowfishTestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/BlowfishTestFile.py new file mode 100644 index 000000000..acbb3603a --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/BlowfishTestFile.py @@ -0,0 +1,4 @@ +from Crypto.Cipher import Blowfish + +key_blowfish = b"0123456789abcdef" +cipher_blowfish = Blowfish.new(key_blowfish, Blowfish.MODE_CBC) # Noncompliant {{(BlockCipher) Blowfish-CBC}} diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/CAST5TestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/CAST5TestFile.py new file mode 100644 index 000000000..dd654fcea --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/CAST5TestFile.py @@ -0,0 +1,4 @@ +from Crypto.Cipher import CAST + +key_cast = b"0123456789abcdef" +cipher_cast = CAST.new(key_cast, CAST.MODE_CBC) # Noncompliant {{(BlockCipher) CAST5-CBC}} diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/ChaCha20Poly1305TestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/ChaCha20Poly1305TestFile.py new file mode 100644 index 000000000..2f69ea0be --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/ChaCha20Poly1305TestFile.py @@ -0,0 +1,7 @@ +from Crypto.Cipher import ChaCha20_Poly1305 + +key_chacha20_poly1305 = b"0123456789abcdef0123456789abcdef" +nonce_chacha20_poly1305 = b"01234567" +cipher_chacha20_poly1305 = ChaCha20_Poly1305.new( # Noncompliant {{(AuthenticatedEncryption) ChaCha20-Poly1305}} + key=key_chacha20_poly1305, + nonce=nonce_chacha20_poly1305) diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/ChaCha20TestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/ChaCha20TestFile.py new file mode 100644 index 000000000..be90c8ffb --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/ChaCha20TestFile.py @@ -0,0 +1,5 @@ +from Crypto.Cipher import ChaCha20 + +key_chacha20 = b"0123456789abcdef0123456789abcdef" +nonce_chacha20 = b"01234567" +cipher_chacha20 = ChaCha20.new(key=key_chacha20, nonce=nonce_chacha20) # Noncompliant {{(StreamCipher) ChaCha20}} diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/DESTestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/DESTestFile.py new file mode 100644 index 000000000..bb956ab8e --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/DESTestFile.py @@ -0,0 +1,5 @@ +from Crypto.Cipher import DES + +key_des = b"01234567" +cipher_des = DES.new(key_des, DES.MODE_ECB) # Noncompliant {{(BlockCipher) DES-56-ECB}} +cipher_des.decrypt(b'some blob') diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/PKCS1OAEPTestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/PKCS1OAEPTestFile.py new file mode 100644 index 000000000..c937ccb62 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/PKCS1OAEPTestFile.py @@ -0,0 +1,6 @@ +from Crypto.Cipher import PKCS1_OAEP +from Crypto.PublicKey import RSA +from Crypto.Hash import SHA256 + +key = RSA.import_key(open('public.pem').read()) +cipher = PKCS1_OAEP.new(key, SHA256) # Noncompliant {{(PublicKeyEncryption) RSA-OAEP}} diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/PKCS1v15TestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/PKCS1v15TestFile.py new file mode 100644 index 000000000..adee56079 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/PKCS1v15TestFile.py @@ -0,0 +1,5 @@ +from Crypto.Cipher import PKCS1_v1_5 +from Crypto.PublicKey import RSA + +key = RSA.importKey(open('public.pem').read()) +cipher = PKCS1_v1_5.new(key) # Noncompliant {{(PublicKeyEncryption) RSA}} diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/RC2TestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/RC2TestFile.py new file mode 100644 index 000000000..9ba68e2c4 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/RC2TestFile.py @@ -0,0 +1,4 @@ +from Crypto.Cipher import ARC2 + +key_arc2 = b"0123456789abcdef" +cipher_arc2 = ARC2.new(key_arc2, ARC2.MODE_CBC) # Noncompliant {{(BlockCipher) RC2-CBC}} diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/RC4TestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/RC4TestFile.py new file mode 100644 index 000000000..c1d08fd5f --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/RC4TestFile.py @@ -0,0 +1,4 @@ +from Crypto.Cipher import ARC4 + +key_arc4 = b"0123456789abcdef" +cipher_arc4 = ARC4.new(key_arc4) # Noncompliant {{(StreamCipher) RC4}} diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/Salsa20TestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/Salsa20TestFile.py new file mode 100644 index 000000000..13d498ade --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/Salsa20TestFile.py @@ -0,0 +1,5 @@ +from Crypto.Cipher import Salsa20 + +key_salsa20 = b"0123456789abcdef0123456789abcdef" +nonce_salsa20 = b"01234567" +cipher_salsa20 = Salsa20.new(key=key_salsa20, nonce=nonce_salsa20) # Noncompliant {{(StreamCipher) Salsa20}} diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/TripleDESTestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/TripleDESTestFile.py new file mode 100644 index 000000000..45ac67190 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/TripleDESTestFile.py @@ -0,0 +1,4 @@ +from Crypto.Cipher import DES3 + +key_3des = b"0123456789abcdef01234567" +cipher_3des = DES3.new(key_3des, DES3.MODE_CBC) # Noncompliant {{(BlockCipher) 3DES-CBC}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/BLAKE2bTestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/BLAKE2bTestFile.py new file mode 100644 index 000000000..190bcdf7f --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/BLAKE2bTestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import BLAKE2b + +hash_blake2b = BLAKE2b.new() # Noncompliant {{(MessageDigest) BLAKE2b}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/BLAKE2sTestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/BLAKE2sTestFile.py new file mode 100644 index 000000000..5b5928fea --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/BLAKE2sTestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import BLAKE2s + +hash_blake2s = BLAKE2s.new() # Noncompliant {{(MessageDigest) BLAKE2s}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/MD2TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/MD2TestFile.py new file mode 100644 index 000000000..8c1894f04 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/MD2TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import MD2 + +hash_md2 = MD2.new() # Noncompliant {{(MessageDigest) MD2}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/MD5TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/MD5TestFile.py new file mode 100644 index 000000000..4d830526b --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/MD5TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import MD5 + +hash_md5 = MD5.new() # Noncompliant {{(MessageDigest) MD5}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/RIPEMD160TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/RIPEMD160TestFile.py new file mode 100644 index 000000000..8c5d05634 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/RIPEMD160TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import RIPEMD160 + +hash_ripemd160 = RIPEMD160.new() # Noncompliant {{(MessageDigest) RIPEMD-160}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/SHA1TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/SHA1TestFile.py new file mode 100644 index 000000000..49966efe3 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/SHA1TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import SHA1 + +hash_sha1 = SHA1.new() # Noncompliant {{(MessageDigest) SHA-1}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/SHA224TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/SHA224TestFile.py new file mode 100644 index 000000000..b6cdab156 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/SHA224TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import SHA224 + +hash_sha224 = SHA224.new() # Noncompliant {{(MessageDigest) SHA-224}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/SHA256TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/SHA256TestFile.py new file mode 100644 index 000000000..46a45ccd2 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/SHA256TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import SHA256 + +hash_sha256 = SHA256.new() # Noncompliant {{(MessageDigest) SHA-256}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/SHA384TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/SHA384TestFile.py new file mode 100644 index 000000000..69c9d06d9 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/SHA384TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import SHA384 + +hash_sha384 = SHA384.new() # Noncompliant {{(MessageDigest) SHA-384}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/SHA3_224TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/SHA3_224TestFile.py new file mode 100644 index 000000000..c539ca916 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/SHA3_224TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import SHA3_224 + +hash_sha3_224 = SHA3_224.new() # Noncompliant {{(MessageDigest) SHA3-224}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/SHA3_256TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/SHA3_256TestFile.py new file mode 100644 index 000000000..c6975d0a1 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/SHA3_256TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import SHA3_256 + +hash_sha3_256 = SHA3_256.new() # Noncompliant {{(MessageDigest) SHA3-256}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/SHA3_384TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/SHA3_384TestFile.py new file mode 100644 index 000000000..03b29d382 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/SHA3_384TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import SHA3_384 + +hash_sha3_384 = SHA3_384.new() # Noncompliant {{(MessageDigest) SHA3-384}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/SHA3_512TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/SHA3_512TestFile.py new file mode 100644 index 000000000..338c5b305 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/SHA3_512TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import SHA3_512 + +hash_sha3_512 = SHA3_512.new() # Noncompliant {{(MessageDigest) SHA3-512}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/SHA512TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/SHA512TestFile.py new file mode 100644 index 000000000..d49f1fe8a --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/SHA512TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import SHA512 + +hash_sha512 = SHA512.new() # Noncompliant {{(MessageDigest) SHA-512}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/TupleHash128TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/TupleHash128TestFile.py new file mode 100644 index 000000000..cfdb54d28 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/TupleHash128TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import TupleHash128 + +hash_t128 = TupleHash128.new() # Noncompliant {{(ExtendableOutputFunction) TupleHash}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/cSHAKE256TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/cSHAKE256TestFile.py new file mode 100644 index 000000000..dc8db14df --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/cSHAKE256TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import cSHAKE256 + +hash_t128 = cSHAKE256.new() # Noncompliant {{(ExtendableOutputFunction) cSHAKE256}} diff --git a/python/src/test/files/rules/detection/pycrypto/kdf/BcryptTestFile.py b/python/src/test/files/rules/detection/pycrypto/kdf/BcryptTestFile.py new file mode 100644 index 000000000..f99740baf --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/kdf/BcryptTestFile.py @@ -0,0 +1,6 @@ +from Crypto.Protocol.KDF import bcrypt + +def test_bcrypt(): + password = b"password" + salt = b"salt1234567890ab" + key = bcrypt(password, cost=10, salt=salt) # detected but not mapped diff --git a/python/src/test/files/rules/detection/pycrypto/kdf/HKDFTestFile.py b/python/src/test/files/rules/detection/pycrypto/kdf/HKDFTestFile.py new file mode 100644 index 000000000..9641cf2aa --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/kdf/HKDFTestFile.py @@ -0,0 +1,7 @@ +from Crypto.Protocol.KDF import HKDF +from Crypto.Hash import SHA512 + +def test_hkdf(): + secret = b"secret" + salt = b"salt1234567890ab" + key = HKDF(secret, 32, salt, SHA512) # Noncompliant {{(KeyDerivationFunction) HKDF-SHA-512}} diff --git a/python/src/test/files/rules/detection/pycrypto/kdf/PBKDF1TestFile.py b/python/src/test/files/rules/detection/pycrypto/kdf/PBKDF1TestFile.py new file mode 100644 index 000000000..12083438e --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/kdf/PBKDF1TestFile.py @@ -0,0 +1,7 @@ +from Crypto.Protocol.KDF import PBKDF1 +from Crypto.Hash import SHA256 + +def test_pbkdf1(): + password = b"password" + salt = b"salt1234" + key = PBKDF1(password, salt, 16, SHA256) # Noncompliant {{(PasswordBasedKeyDerivationFunction) PBKDF1-SHA-256}} diff --git a/python/src/test/files/rules/detection/pycrypto/kdf/PBKDF2TestFile.py b/python/src/test/files/rules/detection/pycrypto/kdf/PBKDF2TestFile.py new file mode 100644 index 000000000..4bbc4e3e7 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/kdf/PBKDF2TestFile.py @@ -0,0 +1,7 @@ +from Crypto.Protocol.KDF import PBKDF2 +from Crypto.Hash import SHA512 + +def test_pbkdf2(): + password = b"password" + salt = b"salt1234" + key = PBKDF2(password, salt, dkLen=64, count=1000, hmac_hash_module=SHA512) # Noncompliant {{(PasswordBasedKeyDerivationFunction) PBKDF2-SHA-512}} diff --git a/python/src/test/files/rules/detection/pycrypto/kdf/SP800108CounterTestFile.py b/python/src/test/files/rules/detection/pycrypto/kdf/SP800108CounterTestFile.py new file mode 100644 index 000000000..13835f210 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/kdf/SP800108CounterTestFile.py @@ -0,0 +1,9 @@ +from Crypto.Protocol.KDF import SP800_108_Counter +from Crypto.Hash import HMAC, SHA256 + +def prf(s, x): + return HMAC.new(s, x, SHA256).digest() # NonCompliant {{(Mac) HMAC-SHA-256}} + +def test_sp800_108_counter(): + secret = b"secret" + key = SP800_108_Counter(secret, 16, prf) # Noncompliant {{(KeyDerivationFunction) SP800_108_CounterKDF}} diff --git a/python/src/test/files/rules/detection/pycrypto/kdf/ScryptTestFile.py b/python/src/test/files/rules/detection/pycrypto/kdf/ScryptTestFile.py new file mode 100644 index 000000000..3dacc3111 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/kdf/ScryptTestFile.py @@ -0,0 +1,6 @@ +from Crypto.Protocol.KDF import scrypt + +def test_scrypt(): + password = b"password" + salt = b"salt1234" + key = scrypt(password, salt, key_len=32, N=2**14, r=8, p=1, num_keys=1) # Noncompliant {{(PasswordBasedKeyDerivationFunction) scrypt}} diff --git a/python/src/test/files/rules/detection/pycrypto/keyagreement/ECDHTestFile.py b/python/src/test/files/rules/detection/pycrypto/keyagreement/ECDHTestFile.py new file mode 100644 index 000000000..4ba7beb6b --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/keyagreement/ECDHTestFile.py @@ -0,0 +1,14 @@ +from Crypto.Hash import SHAKE128 +from Crypto.PublicKey import ECC +from Crypto.Protocol.DH import key_agreement + +def kdf(x): + return SHAKE128.new(x).read(32) # Noncompliant {{(ExtendableOutputFunction) SHAKE128}} + +priv_key = ECC.generate(curve='p256') +pub_key = priv_key.public_key() + +session_key = key_agreement( # Noncompliant {{(KeyAgreement) ECDH}} + kdf=kdf, + static_priv=priv_key, + static_pub=pub_key) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/keyagreement/X25519TestFile.py b/python/src/test/files/rules/detection/pycrypto/keyagreement/X25519TestFile.py new file mode 100644 index 000000000..e99f2d72e --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/keyagreement/X25519TestFile.py @@ -0,0 +1,14 @@ +from Crypto.Hash import SHAKE128 +from Crypto.Protocol.DH import key_agreement, import_x25519_public_key, import_x25519_private_key + +def kdf(x): + return SHAKE128.new(x).read(32) # Noncompliant {{(ExtendableOutputFunction) SHAKE128}} + +pub_key = import_x25519_public_key(b'some 32 bytes public key') +priv_key = import_x25519_private_key(b'some 32 bytes private key') + +session_key = key_agreement( # Noncompliant {{(KeyAgreement) x25519}} + kdf=kdf, + static_priv=priv_key, + static_pub=pub_key) + diff --git a/python/src/test/files/rules/detection/pycrypto/keyagreement/X448TestFile.py b/python/src/test/files/rules/detection/pycrypto/keyagreement/X448TestFile.py new file mode 100644 index 000000000..bef21c389 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/keyagreement/X448TestFile.py @@ -0,0 +1,13 @@ +from Crypto.Hash import SHAKE128 +from Crypto.Protocol.DH import key_agreement, import_x448_public_key, import_x448_private_key + +def kdf(x): + return SHAKE128.new(x).read(32) # Noncompliant {{(ExtendableOutputFunction) SHAKE128}} + +pub_key = import_x448_public_key(b'some 32 bytes public key') +priv_key = import_x448_private_key(b'some 32 bytes private key') + +session_key = key_agreement( # Noncompliant {{(KeyAgreement) x448}} + kdf=kdf, + static_priv=priv_key, + static_pub=pub_key) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/mac/CMACTestFile.py b/python/src/test/files/rules/detection/pycrypto/mac/CMACTestFile.py new file mode 100644 index 000000000..b2524fbfd --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/mac/CMACTestFile.py @@ -0,0 +1,5 @@ +from Crypto.Hash import CMAC +from Crypto.Cipher import AES + +key = b'some key' +cmac = CMAC.new(key, AES) # Noncompliant {{(Mac) CMAC-AES}} diff --git a/python/src/test/files/rules/detection/pycrypto/mac/HMACTestFile.py b/python/src/test/files/rules/detection/pycrypto/mac/HMACTestFile.py new file mode 100644 index 000000000..bc776cb2e --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/mac/HMACTestFile.py @@ -0,0 +1,4 @@ +from Crypto.Hash import HMAC, SHA256 + +secret = b'some secret' +hmac = HMAC.new(secret, digestmod=SHA256) # Noncompliant {{(Mac) HMAC-SHA-256}} diff --git a/python/src/test/files/rules/detection/pycrypto/mac/KMAC128TestFile.py b/python/src/test/files/rules/detection/pycrypto/mac/KMAC128TestFile.py new file mode 100644 index 000000000..fa40ab43a --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/mac/KMAC128TestFile.py @@ -0,0 +1,4 @@ +from Crypto.Hash import KMAC128 + +key = b'Sixteen byte key' +mac = KMAC128.new(key=key) # Noncompliant {{(Mac) KMAC128}} diff --git a/python/src/test/files/rules/detection/pycrypto/mac/KMAC256TestFile.py b/python/src/test/files/rules/detection/pycrypto/mac/KMAC256TestFile.py new file mode 100644 index 000000000..592c3d70d --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/mac/KMAC256TestFile.py @@ -0,0 +1,4 @@ +from Crypto.Hash import KMAC256 + +key = b'Sixteen byte key' +mac = KMAC256.new(key=key) # Noncompliant {{(Mac) KMAC256}} diff --git a/python/src/test/files/rules/detection/pycrypto/mac/Poly1305TestFile.py b/python/src/test/files/rules/detection/pycrypto/mac/Poly1305TestFile.py new file mode 100644 index 000000000..3c27d3cfb --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/mac/Poly1305TestFile.py @@ -0,0 +1,4 @@ +from Crypto.Hash import Poly1305 + +key = b'Sixteen byte key' +mac = Poly1305.new(key) # Noncompliant {{(Mac) Poly1305}} diff --git a/python/src/test/files/rules/detection/pycrypto/publickey/DSATestFile.py b/python/src/test/files/rules/detection/pycrypto/publickey/DSATestFile.py new file mode 100644 index 000000000..1d78c629c --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/publickey/DSATestFile.py @@ -0,0 +1,6 @@ +from Crypto.PublicKey import DSA + +def test_dsa(): + key1 = DSA.generate(bits=2048) # Noncompliant {{(PrivateKey) DSA}} + key2 = DSA.construct((2,3), True) # Noncompliant {{(Key) DSA}} + key3 = DSA.import_key("abcdef") # Noncompliant {{(Key) DSA}} diff --git a/python/src/test/files/rules/detection/pycrypto/publickey/ECCTestFile.py b/python/src/test/files/rules/detection/pycrypto/publickey/ECCTestFile.py new file mode 100644 index 000000000..c8b3d83cf --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/publickey/ECCTestFile.py @@ -0,0 +1,6 @@ +from Crypto.PublicKey import ECC + +def test_ecc(): + key1 = ECC.generate(curve="Ed25519") # Noncompliant {{(PrivateKey) EC-Edwards25519}} + key2 = ECC.construct(curve="Curve448", seed=b"A" * 56) # Noncompliant {{(Key) EC-Curve448}} + key3 = ECC.import_key("abcdef") # Noncompliant {{(Key) EC}} diff --git a/python/src/test/files/rules/detection/pycrypto/publickey/ElGamalTestFile.py b/python/src/test/files/rules/detection/pycrypto/publickey/ElGamalTestFile.py new file mode 100644 index 000000000..c93c49723 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/publickey/ElGamalTestFile.py @@ -0,0 +1,5 @@ +from Crypto.PublicKey import ElGamal + +def test_elgamal(): + key1 = ElGamal.generate(2048, None) # Noncompliant {{(PrivateKey) ElGamal}} + key2 = ElGamal.construct((2,3,4)) # Noncompliant {{(Key) ElGamal}} diff --git a/python/src/test/files/rules/detection/pycrypto/publickey/RSATestFile.py b/python/src/test/files/rules/detection/pycrypto/publickey/RSATestFile.py new file mode 100644 index 000000000..9699b98ad --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/publickey/RSATestFile.py @@ -0,0 +1,6 @@ +from Crypto.PublicKey import RSA + +def test_rsa(): + key1 = RSA.generate(bits=2048) # Noncompliant {{(PrivateKey) RSA}} + key2 = RSA.construct((2,3), True) # Noncompliant {{(Key) RSA}} + key3 = RSA.import_key("abcdef") # Noncompliant {{(Key) RSA}} diff --git a/python/src/test/files/rules/detection/pycrypto/signature/DSSSignTestFile.py b/python/src/test/files/rules/detection/pycrypto/signature/DSSSignTestFile.py new file mode 100644 index 000000000..4a8e56886 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/signature/DSSSignTestFile.py @@ -0,0 +1,9 @@ +from Crypto.Hash import SHA256 +from Crypto.PublicKey import DSA +from Crypto.Signature import DSS + +message = b'some message' +key = DSA.import_key(open('privkey.der').read()) +h = SHA256.new(message) # Noncompliant {{(MessageDigest) SHA-256}} +signer = DSS.new(key, 'fips-186-3') # Noncompliant {{(Signature) DSA-SHA-256}} +signature = signer.sign(h) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/signature/DSSVerifyTestFile.py b/python/src/test/files/rules/detection/pycrypto/signature/DSSVerifyTestFile.py new file mode 100644 index 000000000..9d8c4dfa1 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/signature/DSSVerifyTestFile.py @@ -0,0 +1,15 @@ +from Crypto.Hash import SHA256 +from Crypto.PublicKey import DSA +from Crypto.Signature import DSS + +message = b'some message' +key = DSA.import_key(open('pubkey.der').read()) +h = SHA256.new(message) # Noncompliant {{(MessageDigest) SHA-256}} +verifier = DSS.new(key, 'fips-186-3') # Noncompliant {{(Signature) DSA-SHA-256}} +signature = b'some signature' + +try: + verifier.verify(h, signature) + print("The message is authentic.") +except ValueError: + print("The message is not authentic.") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/signature/ECDSASignTestFile.py b/python/src/test/files/rules/detection/pycrypto/signature/ECDSASignTestFile.py new file mode 100644 index 000000000..368980c82 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/signature/ECDSASignTestFile.py @@ -0,0 +1,9 @@ +from Crypto.Hash import SHA256 +from Crypto.PublicKey import ECC +from Crypto.Signature import DSS + +message = b'some message' +key = ECC.import_key(open('privkey.der').read()) +h = SHA256.new(message) # Noncompliant {{(MessageDigest) SHA-256}} +signer = DSS.new(key, 'fips-186-3') # Noncompliant {{(Signature) ECDSA-SHA-256}} +signature = signer.sign(h) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/signature/ECDSAVerifyTestFile.py b/python/src/test/files/rules/detection/pycrypto/signature/ECDSAVerifyTestFile.py new file mode 100644 index 000000000..6031b9e50 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/signature/ECDSAVerifyTestFile.py @@ -0,0 +1,15 @@ +from Crypto.Hash import SHA256 +from Crypto.PublicKey import ECC +from Crypto.Signature import DSS + +message = b'some message' +key = ECC.import_key(open('pubkey.der').read()) +h = SHA256.new(message) # Noncompliant {{(MessageDigest) SHA-256}} +verifier = DSS.new(key, 'fips-186-3') # Noncompliant {{(Signature) ECDSA-SHA-256}} +signature = b'some signature' + +try: + verifier.verify(h, signature) + print("The message is authentic.") +except ValueError: + print("The message is not authentic.") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/signature/EdDSASignTestFile.py b/python/src/test/files/rules/detection/pycrypto/signature/EdDSASignTestFile.py new file mode 100644 index 000000000..63ede1551 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/signature/EdDSASignTestFile.py @@ -0,0 +1,9 @@ +from Crypto.PublicKey import ECC +from Crypto.Signature import eddsa +from Crypto.Hash import SHA512 + +message = b'some message' +prehashed_message = SHA512.new(message) # Noncompliant {{(MessageDigest) SHA-512}} +key = ECC.import_key(open("private_ed25519.pem").read()) +signer = eddsa.new(key, 'rfc8032') # Noncompliant {{(Signature) EdDSA}} +signature = signer.sign(prehashed_message) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/signature/EdDSAVerifyTestFile.py b/python/src/test/files/rules/detection/pycrypto/signature/EdDSAVerifyTestFile.py new file mode 100644 index 000000000..76189fac5 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/signature/EdDSAVerifyTestFile.py @@ -0,0 +1,14 @@ +from Crypto.PublicKey import ECC +from Crypto.Signature import eddsa +from Crypto.Hash import SHA512 + +signature = b'some signature' +raw_public_bytes = b"\x01" * 32 +public_key = eddsa.import_public_key(raw_public_bytes) +verifier = eddsa.new(public_key, 'rfc8032') # Noncompliant {{(Signature) EdDSA}} + +try: + verifier.verify(h, signature) + print("The message is authentic") +except ValueError: + print("The message is not authentic") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/signature/PKCS1v15SignTestFile.py b/python/src/test/files/rules/detection/pycrypto/signature/PKCS1v15SignTestFile.py new file mode 100644 index 000000000..dadcc8f25 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/signature/PKCS1v15SignTestFile.py @@ -0,0 +1,9 @@ +from Crypto.Signature import pkcs1_15 +from Crypto.Hash import SHA256 +from Crypto.PublicKey import RSA + +message = b'To be signed' +key = RSA.import_key(open('private_key.der').read()) +h = SHA256.new(message) # Noncompliant {{(MessageDigest) SHA-256}} +scheme = pkcs1_15.new(key) # Noncompliant {{(Signature) RSA-PKCS1-1.5-SHA-256}} +signature = scheme.sign(h) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/signature/PKCS1v15VerifyTestFile.py b/python/src/test/files/rules/detection/pycrypto/signature/PKCS1v15VerifyTestFile.py new file mode 100644 index 000000000..c7c0ea214 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/signature/PKCS1v15VerifyTestFile.py @@ -0,0 +1,15 @@ +from Crypto.Signature import pkcs1_15 +from Crypto.Hash import SHA256 +from Crypto.PublicKey import RSA + +message = b'To be signed' +key = RSA.import_key(open('public_key.der').read()) +h = SHA256.new(message) # Noncompliant {{(MessageDigest) SHA-256}} +scheme = pkcs1_15.new(key) # Noncompliant {{(Signature) RSA-PKCS1-1.5-SHA-256}} +signature = b'some sginature' + +try: + scheme.verify(h, signature) + print("The signature is valid.") +except (ValueError, TypeError): + print("The signature is not valid.") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/signature/PSSSignTestFile.py b/python/src/test/files/rules/detection/pycrypto/signature/PSSSignTestFile.py new file mode 100644 index 000000000..5f344b7f0 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/signature/PSSSignTestFile.py @@ -0,0 +1,9 @@ +from Crypto.Signature import pss +from Crypto.Hash import SHA256 +from Crypto.PublicKey import RSA + +message = b'To be signed' +key = RSA.import_key(open('private_key.der').read()) +h = SHA256.new(message) # Noncompliant {{(MessageDigest) SHA-256}} +scheme = pss.new(key) # Noncompliant {{(ProbabilisticSignatureScheme) RSA-PSS}} +signature = scheme.sign(h) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/signature/PSSVerifyTestFile.py b/python/src/test/files/rules/detection/pycrypto/signature/PSSVerifyTestFile.py new file mode 100644 index 000000000..7341f75d7 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/signature/PSSVerifyTestFile.py @@ -0,0 +1,16 @@ +from Crypto.Signature import pss +from Crypto.Signature.pss import PSS_SigScheme +from Crypto.Hash import SHA256 +from Crypto.PublicKey import RSA + +message = b'To be signed' +key = RSA.import_key(open('pubkey.der', 'rb').read()) +h = SHA256.new(message) # Noncompliant {{(MessageDigest) SHA-256}} +verifier = pss.new(key) # Noncompliant {{(ProbabilisticSignatureScheme) RSA-PSS}} +signature = b'some sginature' + +try: + verifier.verify(h, signature) + print("The signature is authentic.") +except (ValueError): + print("The signature is not authentic.") \ No newline at end of file diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/AESTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/AESTest.java new file mode 100644 index 000000000..3f2add569 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/AESTest.java @@ -0,0 +1,106 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mode; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Encrypt; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class AESTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/AESTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("AES"); + + DetectionStore modeStore = + getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); + assertThat(modeStore).isNotNull(); + assertThat(modeStore.getDetectionValues().get(0).asString()).isEqualTo("MODE_CBC"); + + DetectionStore actionStore = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + if (actionStore != null) { + assertThat(actionStore.getDetectionValues().get(0).asString()) + .satisfiesAnyOf( + s -> assertThat(s).isEqualTo("ENCRYPT"), + s -> assertThat(s).isEqualTo("DECRYPT")); + } + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(BlockCipher.class); + assertThat(cipher.getChildren()).hasSize(4); + assertThat(cipher.asString()).isEqualTo("AES-CBC"); + + INode mode = cipher.getChildren().get(Mode.class); + assertThat(mode).isNotNull(); + assertThat(mode.getChildren()).isEmpty(); + assertThat(mode.asString()).isEqualTo("CBC"); + + INode oid = cipher.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.1"); + + INode blockSize = cipher.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("128"); + + INode encrypt = cipher.getChildren().get(Encrypt.class); + assertThat(encrypt).isNotNull(); + assertThat(encrypt.getChildren()).isEmpty(); + assertThat(encrypt.asString()).isEqualTo("ENCRYPT"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/BlowfishTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/BlowfishTest.java new file mode 100644 index 000000000..22cbae93c --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/BlowfishTest.java @@ -0,0 +1,88 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mode; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class BlowfishTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/BlowfishTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("Blowfish"); + + DetectionStore modeStore = + getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); + assertThat(modeStore).isNotNull(); + assertThat(modeStore.getDetectionValues().get(0).asString()).isEqualTo("MODE_CBC"); + + DetectionStore actionStore = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + if (actionStore != null) { + assertThat(actionStore.getDetectionValues().get(0).asString()) + .satisfiesAnyOf( + s -> assertThat(s).isEqualTo("ENCRYPT"), + s -> assertThat(s).isEqualTo("DECRYPT")); + } + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(BlockCipher.class); + assertThat(cipher.getChildren()).hasSize(1); + assertThat(cipher.asString()).isEqualTo("Blowfish-CBC"); + + INode mode = cipher.getChildren().get(Mode.class); + assertThat(mode).isNotNull(); + assertThat(mode.getChildren()).isEmpty(); + assertThat(mode.asString()).isEqualTo("CBC"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/CAST5Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/CAST5Test.java new file mode 100644 index 000000000..5c174d392 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/CAST5Test.java @@ -0,0 +1,94 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mode; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class CAST5Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/CAST5TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("CAST5"); + + DetectionStore modeStore = + getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); + assertThat(modeStore).isNotNull(); + assertThat(modeStore.getDetectionValues().get(0).asString()).isEqualTo("MODE_CBC"); + + DetectionStore actionStore = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + if (actionStore != null) { + assertThat(actionStore.getDetectionValues().get(0).asString()) + .satisfiesAnyOf( + s -> assertThat(s).isEqualTo("ENCRYPT"), + s -> assertThat(s).isEqualTo("DECRYPT")); + } + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(BlockCipher.class); + assertThat(cipher.getChildren()).hasSize(2); + assertThat(cipher.asString()).isEqualTo("CAST5-CBC"); + + INode mode = cipher.getChildren().get(Mode.class); + assertThat(mode).isNotNull(); + assertThat(mode.getChildren()).isEmpty(); + assertThat(mode.asString()).isEqualTo("CBC"); + + INode blockSize = cipher.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("64"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/ChaCha20Poly1305Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/ChaCha20Poly1305Test.java new file mode 100644 index 000000000..2e82ca849 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/ChaCha20Poly1305Test.java @@ -0,0 +1,79 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.AuthenticatedEncryption; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class ChaCha20Poly1305Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/ChaCha20Poly1305TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("ChaCha20Poly1305"); + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(AuthenticatedEncryption.class); + assertThat(cipher.getChildren()).hasSize(1); + assertThat(cipher.asString()).isEqualTo("ChaCha20-Poly1305"); + + INode messageDigest = cipher.getChildren().get(MessageDigest.class); + assertThat(messageDigest).isNotNull(); + assertThat(messageDigest.getChildren()).hasSize(1); + assertThat(messageDigest.asString()).isEqualTo("Poly1305"); + + INode digest = messageDigest.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/ChaCha20Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/ChaCha20Test.java new file mode 100644 index 000000000..0ce2ec9cd --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/ChaCha20Test.java @@ -0,0 +1,67 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.StreamCipher; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class ChaCha20Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/ChaCha20TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("ChaCha20"); + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(StreamCipher.class); + assertThat(cipher.getChildren()).isEmpty(); + assertThat(cipher.asString()).isEqualTo("ChaCha20"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/DESTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/DESTest.java new file mode 100644 index 000000000..cee58d89c --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/DESTest.java @@ -0,0 +1,100 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.Mode; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class DESTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/DESTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("DES"); + + DetectionStore modeStore = + getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); + assertThat(modeStore).isNotNull(); + assertThat(modeStore.getDetectionValues().get(0).asString()).isEqualTo("MODE_ECB"); + + DetectionStore actionStore = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + if (actionStore != null) { + assertThat(actionStore.getDetectionValues().get(0).asString()) + .satisfiesAnyOf( + s -> assertThat(s).isEqualTo("ENCRYPT"), + s -> assertThat(s).isEqualTo("DECRYPT")); + } + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(BlockCipher.class); + assertThat(cipher.getChildren()).hasSize(4); + assertThat(cipher.asString()).isEqualTo("DES-56-ECB"); + + INode mode = cipher.getChildren().get(Mode.class); + assertThat(mode).isNotNull(); + assertThat(mode.getChildren()).isEmpty(); + assertThat(mode.asString()).isEqualTo("ECB"); + + INode keyLength = cipher.getChildren().get(KeyLength.class); + assertThat(keyLength).isNotNull(); + assertThat(keyLength.getChildren()).isEmpty(); + assertThat(keyLength.asString()).isEqualTo("56"); + + INode blockSize = cipher.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("64"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/PKCS1OAEPTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/PKCS1OAEPTest.java new file mode 100644 index 000000000..64e5360eb --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/PKCS1OAEPTest.java @@ -0,0 +1,117 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.Padding; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class PKCS1OAEPTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/PKCS1OAEPTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + // cipher = PKCS1_OAEP.new(key, SHA256) + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("PKCS1_OAEP"); + + DetectionStore actionStore = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + if (actionStore != null) { + assertThat(actionStore.getDetectionValues().get(0).asString()) + .satisfiesAnyOf( + s -> assertThat(s).isEqualTo("ENCRYPT"), + s -> assertThat(s).isEqualTo("DECRYPT")); + } + + DetectionStore keyStore = + getStoreOfValueType(Algorithm.class, detectionStore.getChildren()); + if (keyStore != null) { + assertThat(keyStore.getDetectionValues()).hasSize(1); + assertThat(keyStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + assertThat(keyStore.getDetectionValues().get(0)).isInstanceOf(Algorithm.class); + } + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(PublicKeyEncryption.class); + assertThat(cipher.getChildren()).hasSize(3); + assertThat(cipher.asString()).isEqualTo("RSA-OAEP"); + + INode oid = cipher.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("1.2.840.113549.1.1.7"); + + INode padding = cipher.getChildren().get(Padding.class); + assertThat(padding).isNotNull(); + assertThat(padding.getChildren()).isEmpty(); + assertThat(padding.asString()).isEqualTo("OAEP"); + + INode key = cipher.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("RSA"); + + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + + INode pke = key.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + assertThat(pke.asString()).isEqualTo("RSA"); + + INode pkeOID = pke.getChildren().get(Oid.class); + assertThat(pkeOID).isNotNull(); + assertThat(pkeOID.asString()).isEqualTo("1.2.840.113549.1.1.1"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/PKCS1v15Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/PKCS1v15Test.java new file mode 100644 index 000000000..ae488c6ff --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/PKCS1v15Test.java @@ -0,0 +1,89 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.Padding; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class PKCS1v15Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/PKCS1v15TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("PKCS1_v1_5"); + + DetectionStore actionStore = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + if (actionStore != null) { + assertThat(actionStore.getDetectionValues().get(0).asString()) + .satisfiesAnyOf( + s -> assertThat(s).isEqualTo("ENCRYPT"), + s -> assertThat(s).isEqualTo("DECRYPT")); + } + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(PublicKeyEncryption.class); + assertThat(cipher.getChildren()).hasSize(2); + assertThat(cipher.asString()).isEqualTo("RSA"); + + INode oid = cipher.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("1.2.840.113549.1.1.1"); + + INode padding = cipher.getChildren().get(Padding.class); + assertThat(padding).isNotNull(); + assertThat(padding.getChildren()).isEmpty(); + assertThat(padding.asString()).isEqualTo("PKCS1"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/RC2Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/RC2Test.java new file mode 100644 index 000000000..bd456aa8b --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/RC2Test.java @@ -0,0 +1,88 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mode; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class RC2Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/RC2TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("RC2"); + + DetectionStore modeStore = + getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); + assertThat(modeStore).isNotNull(); + assertThat(modeStore.getDetectionValues().get(0).asString()).isEqualTo("MODE_CBC"); + + DetectionStore actionStore = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + if (actionStore != null) { + assertThat(actionStore.getDetectionValues().get(0).asString()) + .satisfiesAnyOf( + s -> assertThat(s).isEqualTo("ENCRYPT"), + s -> assertThat(s).isEqualTo("DECRYPT")); + } + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(BlockCipher.class); + assertThat(cipher.getChildren()).hasSize(1); + assertThat(cipher.asString()).isEqualTo("RC2-CBC"); + + INode mode = cipher.getChildren().get(Mode.class); + assertThat(mode).isNotNull(); + assertThat(mode.getChildren()).isEmpty(); + assertThat(mode.asString()).isEqualTo("CBC"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/RC4Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/RC4Test.java new file mode 100644 index 000000000..c5e0b7336 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/RC4Test.java @@ -0,0 +1,77 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.StreamCipher; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class RC4Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/RC4TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("RC4"); + + DetectionStore actionStore = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + if (actionStore != null) { + assertThat(actionStore.getDetectionValues().get(0).asString()) + .satisfiesAnyOf( + s -> assertThat(s).isEqualTo("ENCRYPT"), + s -> assertThat(s).isEqualTo("DECRYPT")); + } + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(StreamCipher.class); + assertThat(cipher.getChildren()).isEmpty(); + assertThat(cipher.asString()).isEqualTo("RC4"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/Salsa20Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/Salsa20Test.java new file mode 100644 index 000000000..5805dcca0 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/Salsa20Test.java @@ -0,0 +1,83 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.StreamCipher; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class Salsa20Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/Salsa20TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("Salsa20"); + + DetectionStore actionStore = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + if (actionStore != null) { + assertThat(actionStore.getDetectionValues().get(0).asString()) + .satisfiesAnyOf( + s -> assertThat(s).isEqualTo("ENCRYPT"), + s -> assertThat(s).isEqualTo("DECRYPT")); + } + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(StreamCipher.class); + assertThat(cipher.getChildren()).hasSize(1); + assertThat(cipher.asString()).isEqualTo("Salsa20"); + + INode keyLength = cipher.getChildren().get(KeyLength.class); + assertThat(keyLength).isNotNull(); + assertThat(keyLength.getChildren()).isEmpty(); + assertThat(keyLength.asString()).isEqualTo("128"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/TripleDESTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/TripleDESTest.java new file mode 100644 index 000000000..2437eb099 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/TripleDESTest.java @@ -0,0 +1,84 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mode; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class TripleDESTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/TripleDESTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("3DES"); + + DetectionStore modeStore = + getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); + assertThat(modeStore).isNotNull(); + assertThat(modeStore.getDetectionValues().get(0).asString()).isEqualTo("MODE_CBC"); + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(BlockCipher.class); + assertThat(cipher.getChildren()).hasSize(2); + assertThat(cipher.asString()).isEqualTo("3DES-CBC"); + + INode mode = cipher.getChildren().get(Mode.class); + assertThat(mode).isNotNull(); + assertThat(mode.getChildren()).isEmpty(); + assertThat(mode.asString()).isEqualTo("CBC"); + + INode blockSize = cipher.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("64"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/BLAKE2bTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/BLAKE2bTest.java new file mode 100644 index 000000000..f1c54fe17 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/BLAKE2bTest.java @@ -0,0 +1,80 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.SaltLength; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class BLAKE2bTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/BLAKE2bTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("BLAKE2b"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(2); + assertThat(md.asString()).isEqualTo("BLAKE2b"); + + INode saltLength = md.getChildren().get(SaltLength.class); + assertThat(saltLength).isNotNull(); + assertThat(saltLength.getChildren()).isEmpty(); + assertThat(saltLength.asString()).isEqualTo("128"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/BLAKE2sTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/BLAKE2sTest.java new file mode 100644 index 000000000..53a984914 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/BLAKE2sTest.java @@ -0,0 +1,80 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.SaltLength; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class BLAKE2sTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/BLAKE2sTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("BLAKE2s"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(2); + assertThat(md.asString()).isEqualTo("BLAKE2s"); + + INode saltLength = md.getChildren().get(SaltLength.class); + assertThat(saltLength).isNotNull(); + assertThat(saltLength.getChildren()).isEmpty(); + assertThat(saltLength.asString()).isEqualTo("64"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/MD2Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/MD2Test.java new file mode 100644 index 000000000..9603d7ff5 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/MD2Test.java @@ -0,0 +1,86 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class MD2Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/MD2TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("MD2"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(3); + assertThat(md.asString()).isEqualTo("MD2"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("128"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("128"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/MD5Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/MD5Test.java new file mode 100644 index 000000000..4841ea4b2 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/MD5Test.java @@ -0,0 +1,86 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class MD5Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/MD5TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("MD5"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(3); + assertThat(md.asString()).isEqualTo("MD5"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("128"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/RIPEMD160Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/RIPEMD160Test.java new file mode 100644 index 000000000..9ae8ab849 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/RIPEMD160Test.java @@ -0,0 +1,80 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class RIPEMD160Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/RIPEMD160TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("RIPEMD160"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(2); + assertThat(md.asString()).isEqualTo("RIPEMD-160"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("160"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA1Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA1Test.java new file mode 100644 index 000000000..43f1f8a88 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA1Test.java @@ -0,0 +1,92 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SHA1Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/SHA1TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SHA1"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA-1"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + + INode oid = md.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("1.3.14.3.2.26"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("160"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA224Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA224Test.java new file mode 100644 index 000000000..55dc5bb4c --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA224Test.java @@ -0,0 +1,92 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SHA224Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/SHA224TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SHA224"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA-224"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + + INode oid = md.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.2.4"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("224"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA256Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA256Test.java new file mode 100644 index 000000000..7984cf644 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA256Test.java @@ -0,0 +1,92 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SHA256Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/SHA256TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SHA256"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA-256"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + + INode oid = md.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("256"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA384Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA384Test.java new file mode 100644 index 000000000..8a4698440 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA384Test.java @@ -0,0 +1,92 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SHA384Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/SHA384TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SHA384"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA-384"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("1024"); + + INode oid = md.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.2.2"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("384"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_224Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_224Test.java new file mode 100644 index 000000000..028159f49 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_224Test.java @@ -0,0 +1,92 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SHA3_224Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/SHA3_224TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SHA3_224"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA3-224"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("1152"); + + INode oid = md.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.2.7"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("224"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_256Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_256Test.java new file mode 100644 index 000000000..59872895c --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_256Test.java @@ -0,0 +1,92 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SHA3_256Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/SHA3_256TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SHA3_256"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA3-256"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("1088"); + + INode oid = md.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.2.8"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("256"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_384Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_384Test.java new file mode 100644 index 000000000..15e7518cb --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_384Test.java @@ -0,0 +1,92 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SHA3_384Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/SHA3_384TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SHA3_384"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA3-384"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("832"); + + INode oid = md.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.2.9"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("384"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_512Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_512Test.java new file mode 100644 index 000000000..1b757a736 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_512Test.java @@ -0,0 +1,92 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SHA3_512Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/SHA3_512TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SHA3_512"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA3-512"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("576"); + + INode oid = md.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.2.10"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("512"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA512Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA512Test.java new file mode 100644 index 000000000..9d0b7d92d --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA512Test.java @@ -0,0 +1,92 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SHA512Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/SHA512TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SHA512"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA-512"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("1024"); + + INode oid = md.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.2.3"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("512"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/TupleHash128Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/TupleHash128Test.java new file mode 100644 index 000000000..4ba2c2ad0 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/TupleHash128Test.java @@ -0,0 +1,80 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class TupleHash128Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/TupleHash128TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("TupleHash128"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(2); + assertThat(md.asString()).isEqualTo("TupleHash"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("128"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/cSHAKE256Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/cSHAKE256Test.java new file mode 100644 index 000000000..82a4b7db7 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/cSHAKE256Test.java @@ -0,0 +1,80 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.ParameterSetIdentifier; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class cSHAKE256Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/cSHAKE256TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("cSHAKE256"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(2); + assertThat(md.asString()).isEqualTo("cSHAKE256"); + + INode parameterSetIdentifier = md.getChildren().get(ParameterSetIdentifier.class); + assertThat(parameterSetIdentifier).isNotNull(); + assertThat(parameterSetIdentifier.getChildren()).isEmpty(); + assertThat(parameterSetIdentifier.asString()).isEqualTo("256"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/HKDFTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/HKDFTest.java new file mode 100644 index 000000000..d58e52ca7 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/HKDFTest.java @@ -0,0 +1,102 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.kdf; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyDerivationFunction; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.algorithms.HKDF; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyDerivation; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class HKDFTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/kdf/HKDFTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("HKDF"); + + DetectionStore keySizeStore = + getStoreOfValueType(KeySize.class, detectionStore.getChildren()); + assertThat(keySizeStore).isNotNull(); + assertThat(keySizeStore.getDetectionValues().get(0).asString()).isEqualTo("256"); + + DetectionStore algorithmStore = + getStoreOfValueType(Algorithm.class, detectionStore.getChildren()); + assertThat(algorithmStore).isNotNull(); + assertThat(algorithmStore.getDetectionValues().get(0).asString()).isEqualTo("SHA512"); + + assertThat(nodes).hasSize(1); + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(KeyDerivationFunction.class); + assertThat(root).isInstanceOf(HKDF.class); + assertThat(root.getChildren()).hasSize(3); + assertThat(root.asString()).isEqualTo("HKDF-SHA-512"); + + INode md = root.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA-512"); + assertThat(md.getChildren().get(DigestSize.class).asString()).isEqualTo("512"); + assertThat(md.getChildren().get(Oid.class).asString()).isEqualTo("2.16.840.1.101.3.4.2.3"); + assertThat(md.getChildren().get(Digest.class).asString()).isEqualTo("DIGEST"); + assertThat(md.getChildren().get(BlockSize.class).asString()).isEqualTo("1024"); + + assertThat(root.getChildren().get(KeyLength.class).asString()).isEqualTo("256"); + assertThat(root.getChildren().get(KeyDerivation.class).asString()) + .isEqualTo("KEYDERIVATION"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/PBKDF1Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/PBKDF1Test.java new file mode 100644 index 000000000..50318602e --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/PBKDF1Test.java @@ -0,0 +1,117 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.kdf; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PasswordBasedKeyDerivationFunction; +import com.ibm.mapper.model.algorithms.PBKDF1; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyDerivation; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class PBKDF1Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/kdf/PBKDF1TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("PBKDF1"); + + DetectionStore keySizeStore = + getStoreOfValueType(KeySize.class, detectionStore.getChildren()); + assertThat(keySizeStore).isNotNull(); + assertThat(keySizeStore.getDetectionValues()).hasSize(1); + assertThat(keySizeStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + assertThat(keySizeStore.getDetectionValues().get(0)).isInstanceOf(KeySize.class); + assertThat(keySizeStore.getDetectionValues().get(0).asString()).isEqualTo("128"); + + DetectionStore algorithmStore = + getStoreOfValueType(Algorithm.class, detectionStore.getChildren()); + assertThat(algorithmStore).isNotNull(); + assertThat(algorithmStore.getDetectionValues()).hasSize(1); + assertThat(algorithmStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + assertThat(algorithmStore.getDetectionValues().get(0)).isInstanceOf(Algorithm.class); + assertThat(algorithmStore.getDetectionValues().get(0).asString()).isEqualTo("SHA256"); + + assertThat(nodes).hasSize(1); + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(PasswordBasedKeyDerivationFunction.class); + assertThat(root).isInstanceOf(PBKDF1.class); + assertThat(root.getChildren()).hasSize(3); + assertThat(root.asString()).isEqualTo("PBKDF1-SHA-256"); + + INode md = root.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA-256"); + + assertThat(md.getChildren().get(DigestSize.class)).isNotNull(); + assertThat(md.getChildren().get(DigestSize.class).asString()).isEqualTo("256"); + assertThat(md.getChildren().get(Oid.class)).isNotNull(); + assertThat(md.getChildren().get(Oid.class).asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + assertThat(md.getChildren().get(Digest.class)).isNotNull(); + assertThat(md.getChildren().get(Digest.class).asString()).isEqualTo("DIGEST"); + assertThat(md.getChildren().get(BlockSize.class)).isNotNull(); + assertThat(md.getChildren().get(BlockSize.class).asString()).isEqualTo("512"); + + assertThat(root.getChildren().get(KeyLength.class)).isNotNull(); + assertThat(root.getChildren().get(KeyLength.class).asString()).isEqualTo("128"); + assertThat(root.getChildren().get(KeyDerivation.class)).isNotNull(); + assertThat(root.getChildren().get(KeyDerivation.class).asString()) + .isEqualTo("KEYDERIVATION"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/PBKDF2Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/PBKDF2Test.java new file mode 100644 index 000000000..429a251f7 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/PBKDF2Test.java @@ -0,0 +1,111 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.kdf; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.IterationCount; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.NumberOfIterations; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PasswordBasedKeyDerivationFunction; +import com.ibm.mapper.model.algorithms.PBKDF2; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyDerivation; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class PBKDF2Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/kdf/PBKDF2TestFile.py", this); + } + + @Override + @SuppressWarnings("unchecked") + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("PBKDF2"); + + DetectionStore iterStore = + getStoreOfValueType(IterationCount.class, detectionStore.getChildren()); + assertThat(iterStore).isNotNull(); + assertThat(iterStore.getDetectionValues().get(0).asString()).isEqualTo("1000"); + + DetectionStore keySizeStore = + getStoreOfValueType(KeySize.class, detectionStore.getChildren()); + assertThat(keySizeStore).isNotNull(); + assertThat(keySizeStore.getDetectionValues().get(0).asString()).isEqualTo("512"); + + DetectionStore algorithmStore = + getStoreOfValueType(Algorithm.class, detectionStore.getChildren()); + assertThat(algorithmStore).isNotNull(); + assertThat(algorithmStore.getDetectionValues().get(0).asString()).isEqualTo("SHA512"); + + assertThat(nodes).hasSize(1); + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(PasswordBasedKeyDerivationFunction.class); + assertThat(root).isInstanceOf(PBKDF2.class); + assertThat(root.getChildren()).hasSize(4); + assertThat(root.asString()).isEqualTo("PBKDF2-SHA-512"); + + INode md = root.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA-512"); + assertThat(md.getChildren().get(DigestSize.class).asString()).isEqualTo("512"); + assertThat(md.getChildren().get(Oid.class).asString()).isEqualTo("2.16.840.1.101.3.4.2.3"); + assertThat(md.getChildren().get(Digest.class).asString()).isEqualTo("DIGEST"); + assertThat(md.getChildren().get(BlockSize.class).asString()).isEqualTo("1024"); + + assertThat(root.getChildren().get(KeyLength.class).asString()).isEqualTo("512"); + assertThat(root.getChildren().get(KeyDerivation.class).asString()) + .isEqualTo("KEYDERIVATION"); + assertThat(root.getChildren().get(NumberOfIterations.class).asString()).isEqualTo("1000"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/SP800108CounterTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/SP800108CounterTest.java new file mode 100644 index 000000000..f30fec7d2 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/SP800108CounterTest.java @@ -0,0 +1,82 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.kdf; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyDerivationFunction; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.algorithms.KDFCounter; +import com.ibm.mapper.model.functionality.KeyDerivation; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SP800108CounterTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/kdf/SP800108CounterTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 1) { + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SP800_108_Counter"); + + DetectionStore keySizeStore = + getStoreOfValueType(KeySize.class, detectionStore.getChildren()); + assertThat(keySizeStore).isNotNull(); + assertThat(keySizeStore.getDetectionValues().get(0).asString()).isEqualTo("128"); + + assertThat(nodes).hasSize(1); + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(KeyDerivationFunction.class); + assertThat(root).isInstanceOf(KDFCounter.class); + assertThat(root.getChildren()).hasSize(2); + assertThat(root.asString()).isEqualTo("SP800_108_CounterKDF"); + + assertThat(root.getChildren().get(KeyLength.class).asString()).isEqualTo("128"); + assertThat(root.getChildren().get(KeyDerivation.class).asString()) + .isEqualTo("KEYDERIVATION"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/ScryptTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/ScryptTest.java new file mode 100644 index 000000000..9aaac66ae --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/ScryptTest.java @@ -0,0 +1,82 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.kdf; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.PasswordBasedKeyDerivationFunction; +import com.ibm.mapper.model.algorithms.Scrypt; +import com.ibm.mapper.model.functionality.KeyDerivation; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class ScryptTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/kdf/ScryptTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("scrypt"); + + DetectionStore keySizeStore = + getStoreOfValueType(KeySize.class, detectionStore.getChildren()); + assertThat(keySizeStore).isNotNull(); + assertThat(keySizeStore.getDetectionValues().get(0).asString()).isEqualTo("256"); + + assertThat(nodes).hasSize(1); + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(PasswordBasedKeyDerivationFunction.class); + assertThat(root).isInstanceOf(Scrypt.class); + assertThat(root.getChildren()).hasSize(2); + assertThat(root.asString()).isEqualTo("scrypt"); + + assertThat(root.getChildren().get(KeyLength.class).asString()).isEqualTo("256"); + assertThat(root.getChildren().get(KeyDerivation.class).asString()) + .isEqualTo("KEYDERIVATION"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/ECDHTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/ECDHTest.java new file mode 100644 index 000000000..98ca80bc8 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/ECDHTest.java @@ -0,0 +1,82 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.keyagreement; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyAgreementContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyAgreement; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.PublicKey; +import com.ibm.mapper.model.algorithms.ECDH; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class ECDHTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/keyagreement/ECDHTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 1) { + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyAgreementContext.class); + assertThat(detectionStore.getDetectionValues().get(0)).isInstanceOf(ValueAction.class); + assertThat(detectionStore.getDetectionValues().get(0).asString()).isEqualTo("ECDH"); + + assertThat(nodes).hasSize(1); + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(KeyAgreement.class); + assertThat(root).isInstanceOf(ECDH.class); + assertThat(root.asString()).isEqualTo("ECDH"); + + assertThat(root.getChildren().get(Oid.class)).isNotNull(); + assertThat(root.getChildren().get(Oid.class).asString()).isEqualTo("1.3.132.1.12"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + assertThat(root.getChildren().get(KeyGeneration.class).asString()) + .isEqualTo("KEYGENERATION"); + assertThat(root.getChildren().get(PublicKey.class)).isNotNull(); + assertThat(root.getChildren().get(PublicKey.class).asString()).isEqualTo("EC"); + assertThat(root.getChildren().get(PrivateKey.class)).isNotNull(); + assertThat(root.getChildren().get(PrivateKey.class).asString()) + .isEqualTo("EC-secp256r1"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/X25519Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/X25519Test.java new file mode 100644 index 000000000..0417b028f --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/X25519Test.java @@ -0,0 +1,87 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.keyagreement; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyAgreementContext; +import com.ibm.mapper.model.EllipticCurve; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyAgreement; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.PublicKey; +import com.ibm.mapper.model.algorithms.X25519; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class X25519Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/keyagreement/X25519TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 1) { + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyAgreementContext.class); + assertThat(detectionStore.getDetectionValues().get(0)).isInstanceOf(ValueAction.class); + assertThat(detectionStore.getDetectionValues().get(0).asString()).isEqualTo("ECDH"); + + assertThat(nodes).hasSize(1); + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(KeyAgreement.class); + assertThat(root).isInstanceOf(X25519.class); + assertThat(root.asString()).isEqualTo("x25519"); + + assertThat(root.getChildren().get(Oid.class)).isNotNull(); + assertThat(root.getChildren().get(Oid.class).asString()).isEqualTo("1.3.101.110"); + assertThat(root.getChildren().get(EllipticCurve.class)).isNotNull(); + assertThat(root.getChildren().get(EllipticCurve.class).asString()) + .isEqualTo("Curve25519"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + assertThat(root.getChildren().get(KeyGeneration.class).asString()) + .isEqualTo("KEYGENERATION"); + assertThat(root.getChildren().get(PublicKey.class)).isNotNull(); + assertThat(root.getChildren().get(PublicKey.class).asString()) + .isEqualTo("EC-Curve25519"); + assertThat(root.getChildren().get(PrivateKey.class)).isNotNull(); + assertThat(root.getChildren().get(PrivateKey.class).asString()) + .isEqualTo("EC-Curve25519"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/X448Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/X448Test.java new file mode 100644 index 000000000..ba0be8725 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/X448Test.java @@ -0,0 +1,86 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.keyagreement; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyAgreementContext; +import com.ibm.mapper.model.EllipticCurve; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyAgreement; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.PublicKey; +import com.ibm.mapper.model.algorithms.X448; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class X448Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/keyagreement/X448TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 1) { + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyAgreementContext.class); + assertThat(detectionStore.getDetectionValues().get(0)).isInstanceOf(ValueAction.class); + assertThat(detectionStore.getDetectionValues().get(0).asString()).isEqualTo("ECDH"); + + assertThat(nodes).hasSize(1); + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(KeyAgreement.class); + assertThat(root).isInstanceOf(X448.class); + assertThat(root.asString()).isEqualTo("x448"); + + assertThat(root.getChildren().get(Oid.class)).isNotNull(); + assertThat(root.getChildren().get(Oid.class).asString()).isEqualTo("1.3.101.111"); + assertThat(root.getChildren().get(EllipticCurve.class)).isNotNull(); + assertThat(root.getChildren().get(EllipticCurve.class).asString()) + .isEqualTo("Curve448"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + assertThat(root.getChildren().get(KeyGeneration.class).asString()) + .isEqualTo("KEYGENERATION"); + assertThat(root.getChildren().get(PublicKey.class)).isNotNull(); + assertThat(root.getChildren().get(PublicKey.class).asString()).isEqualTo("EC-Curve448"); + assertThat(root.getChildren().get(PrivateKey.class)).isNotNull(); + assertThat(root.getChildren().get(PrivateKey.class).asString()) + .isEqualTo("EC-Curve448"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/CMACTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/CMACTest.java new file mode 100644 index 000000000..5daf5dbff --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/CMACTest.java @@ -0,0 +1,92 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.mac; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.context.MacContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mac; +import com.ibm.mapper.model.Oid; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class CMACTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/mac/CMACTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(MacContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(Algorithm.class); + assertThat(value.asString()).isEqualTo("AES"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + INode mac = nodes.get(0); + assertThat(mac).isInstanceOf(Mac.class); + assertThat(mac.asString()).isEqualTo("CMAC-AES"); + assertThat(mac.getChildren()).hasSize(2); + + INode cipher = mac.getChildren().get(BlockCipher.class); + assertThat(cipher).isNotNull(); + assertThat(cipher.asString()).isEqualTo("AES"); + + INode blockSize = cipher.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("128"); + + INode oid = cipher.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.1"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/HMACTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/HMACTest.java new file mode 100644 index 000000000..b62ec4557 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/HMACTest.java @@ -0,0 +1,91 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.mac; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.context.MacContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mac; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class HMACTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/mac/HMACTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(MacContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(Algorithm.class); + assertThat(value.asString()).isEqualTo("SHA256"); + + assertThat(nodes).hasSize(1); + INode mac = nodes.get(0); + assertThat(mac).isInstanceOf(Mac.class); + assertThat(mac.asString()).isEqualTo("HMAC-SHA-256"); + assertThat(mac.getChildren()).hasSize(3); + + INode digest = mac.getChildren().get(MessageDigest.class); + assertThat(digest).isNotNull(); + assertThat(digest.asString()).isEqualTo("SHA-256"); + assertThat(digest.getChildren()).hasSize(4); + + assertThat(digest.getChildren().get(Digest.class)).isNotNull(); + assertThat(digest.getChildren().get(Digest.class).asString()).isEqualTo("DIGEST"); + assertThat(digest.getChildren().get(BlockSize.class)).isNotNull(); + assertThat(digest.getChildren().get(BlockSize.class).asString()).isEqualTo("512"); + assertThat(digest.getChildren().get(Oid.class)).isNotNull(); + assertThat(digest.getChildren().get(Oid.class).asString()) + .isEqualTo("2.16.840.1.101.3.4.2.1"); + assertThat(digest.getChildren().get(DigestSize.class)).isNotNull(); + assertThat(digest.getChildren().get(DigestSize.class).asString()).isEqualTo("256"); + + assertThat(mac.getChildren().get(Oid.class)).isNotNull(); + assertThat(mac.getChildren().get(Oid.class).asString()).isEqualTo("1.2.840.113549.2.9"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/KMAC128Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/KMAC128Test.java new file mode 100644 index 000000000..0e471fd74 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/KMAC128Test.java @@ -0,0 +1,99 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.mac; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.MacContext; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.ExtendableOutputFunction; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mac; +import com.ibm.mapper.model.ParameterSetIdentifier; +import com.ibm.mapper.model.algorithms.KMAC; +import com.ibm.mapper.model.algorithms.shake.CSHAKE; +import com.ibm.mapper.model.functionality.Tag; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class KMAC128Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/mac/KMAC128TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(MacContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("KMAC128"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + INode mac = nodes.get(0); + assertThat(mac).isInstanceOf(KMAC.class); + assertThat(mac.getKind()).isEqualTo(Mac.class); + assertThat(mac.asString()).isEqualTo("KMAC128"); + assertThat(mac.getChildren()).hasSize(4); + + INode parameterSetIdentifier = mac.getChildren().get(ParameterSetIdentifier.class); + assertThat(parameterSetIdentifier).isNotNull(); + assertThat(parameterSetIdentifier.asString()).isEqualTo("128"); + + INode digestSize = mac.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("256"); + + INode cshake = mac.getChildren().get(ExtendableOutputFunction.class); + assertThat(cshake).isNotNull(); + assertThat(cshake).isInstanceOf(CSHAKE.class); + assertThat(cshake.asString()).isEqualTo("cSHAKE128"); + + INode tag = mac.getChildren().get(Tag.class); + assertThat(tag).isNotNull(); + assertThat(tag.asString()).isEqualTo("TAG"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/KMAC256Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/KMAC256Test.java new file mode 100644 index 000000000..62a6987ee --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/KMAC256Test.java @@ -0,0 +1,99 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.mac; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.MacContext; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.ExtendableOutputFunction; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mac; +import com.ibm.mapper.model.ParameterSetIdentifier; +import com.ibm.mapper.model.algorithms.KMAC; +import com.ibm.mapper.model.algorithms.shake.CSHAKE; +import com.ibm.mapper.model.functionality.Tag; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class KMAC256Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/mac/KMAC256TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(MacContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("KMAC256"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + INode mac = nodes.get(0); + assertThat(mac).isInstanceOf(KMAC.class); + assertThat(mac.getKind()).isEqualTo(Mac.class); + assertThat(mac.asString()).isEqualTo("KMAC256"); + assertThat(mac.getChildren()).hasSize(4); + + INode parameterSetIdentifier = mac.getChildren().get(ParameterSetIdentifier.class); + assertThat(parameterSetIdentifier).isNotNull(); + assertThat(parameterSetIdentifier.asString()).isEqualTo("256"); + + INode digestSize = mac.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("512"); + + INode cshake = mac.getChildren().get(ExtendableOutputFunction.class); + assertThat(cshake).isNotNull(); + assertThat(cshake).isInstanceOf(CSHAKE.class); + assertThat(cshake.asString()).isEqualTo("cSHAKE256"); + + INode tag = mac.getChildren().get(Tag.class); + assertThat(tag).isNotNull(); + assertThat(tag.asString()).isEqualTo("TAG"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/Poly1305Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/Poly1305Test.java new file mode 100644 index 000000000..556e26eca --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/Poly1305Test.java @@ -0,0 +1,80 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.mac; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.MacContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mac; +import com.ibm.mapper.model.functionality.Tag; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class Poly1305Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/mac/Poly1305TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(MacContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("Poly1305"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + INode mac = nodes.get(0); + assertThat(mac.getKind()).isEqualTo(Mac.class); + assertThat(mac.asString()).isEqualTo("Poly1305"); + assertThat(mac.getChildren()).hasSize(1); + + INode tag = mac.getChildren().get(Tag.class); + assertThat(tag).isNotNull(); + assertThat(tag.asString()).isEqualTo("TAG"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/DSATest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/DSATest.java new file mode 100644 index 000000000..54e77578a --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/DSATest.java @@ -0,0 +1,143 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.publickey; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class DSATest extends TestBase { + + public DSATest() { + super(PythonCryptoPublicKey.DSARules()); + } + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/publickey/DSATestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + switch (findingId) { + case 0 -> { + // DSA.generate(bits=2048) + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(PrivateKeyContext.class); + IValue kSz = detectionStore.getDetectionValues().get(0); + assertThat(kSz).isInstanceOf(KeySize.class); + assertThat(((KeySize) kSz).asString()).isEqualTo("2048"); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(PrivateKey.class); + assertThat(root.getChildren()).hasSize(3); + assertThat(root.asString()).isEqualTo("DSA"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + + INode sig = root.getChildren().get(Signature.class); + assertThat(sig).isNotNull(); + assertThat(sig.getChildren().get(Oid.class).asString()) + .isEqualTo("1.2.840.10040.4.1"); + + INode kgen = root.getChildren().get(KeyGeneration.class); + assertThat(kgen).isNotNull(); + + INode klen = root.getChildren().get(KeyLength.class); + assertThat(klen).isNotNull(); + assertThat(klen.asString()).isEqualTo("2048"); + } + case 1 -> { + // DSA.construct(...) + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyContext.class); + assertThat( + ((KeyAction) detectionStore.getDetectionValues().get(0)) + .getAction()) + .isEqualTo(KeyAction.Action.GENERATION); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(Key.class); + assertThat(root.getChildren()).hasSize(2); + assertThat(root.asString()).isEqualTo("DSA"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + assertThat( + root.getChildren() + .get(Signature.class) + .getChildren() + .get(Oid.class) + .asString()) + .isEqualTo("1.2.840.10040.4.1"); + } + case 2 -> { + // DSA.import_key(...) + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyContext.class); + assertThat( + ((KeyAction) detectionStore.getDetectionValues().get(0)) + .getAction()) + .isEqualTo(KeyAction.Action.GENERATION); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(Key.class); + assertThat(root.getChildren()).hasSize(2); + assertThat(root.asString()).isEqualTo("DSA"); + assertThat( + root.getChildren() + .get(Signature.class) + .getChildren() + .get(Oid.class) + .asString()) + .isEqualTo("1.2.840.10040.4.1"); + } + default -> throw new AssertionError("Unexpected findingId: " + findingId); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/ECCTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/ECCTest.java new file mode 100644 index 000000000..4397a1e9f --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/ECCTest.java @@ -0,0 +1,142 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.publickey; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Curve; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.mapper.model.EllipticCurve; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class ECCTest extends TestBase { + + public ECCTest() { + super(PythonCryptoPublicKey.ECCRules()); + } + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/publickey/ECCTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + switch (findingId) { + case 0 -> { + // ECC.generate(curve="Ed25519") + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(PrivateKeyContext.class); + IValue v = detectionStore.getDetectionValues().get(0); + assertThat(v).isInstanceOf(Curve.class); + assertThat(v.asString()).isEqualTo("Ed25519"); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(PrivateKey.class); + assertThat(root.getChildren()).hasSize(2); + assertThat(root.asString()).isEqualTo("EC-Edwards25519"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + + INode pke = root.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + assertThat(pke.getChildren()).hasSize(2); + assertThat(pke.asString()).isEqualTo("EC-Edwards25519"); + assertThat(pke.getChildren().get(EllipticCurve.class).asString()) + .isEqualTo("Edwards25519"); + assertThat(pke.getChildren().get(Oid.class).asString()) + .isEqualTo("1.2.840.10045.2.1"); + } + case 1 -> { + // ECC.construct(curve="Curve448", seed=b"A" * 56) + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyContext.class); + IValue v = detectionStore.getDetectionValues().get(0); + assertThat(v).isInstanceOf(Curve.class); + assertThat(v.asString()).isEqualTo("Curve448"); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(Key.class); + assertThat(root.getChildren()).hasSize(2); + assertThat(root.asString()).isEqualTo("EC-Curve448"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + + INode pke = root.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + assertThat(pke.getChildren()).hasSize(2); + assertThat(pke.asString()).isEqualTo("EC-Curve448"); + assertThat(pke.getChildren().get(EllipticCurve.class).asString()) + .isEqualTo("Curve448"); + assertThat(pke.getChildren().get(Oid.class).asString()) + .isEqualTo("1.2.840.10045.2.1"); + } + case 2 -> { + // ECC.import_key(...) + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyContext.class); + assertThat( + ((KeyAction) detectionStore.getDetectionValues().get(0)) + .getAction()) + .isEqualTo(KeyAction.Action.GENERATION); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(Key.class); + assertThat(root.getChildren()).hasSize(2); + assertThat(root.asString()).isEqualTo("EC"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + assertThat( + root.getChildren() + .get(PublicKeyEncryption.class) + .getChildren() + .get(Oid.class) + .asString()) + .isEqualTo("1.2.840.10045.2.1"); + } + default -> throw new AssertionError("Unexpected findingId: " + findingId); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/ElGamalTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/ElGamalTest.java new file mode 100644 index 000000000..050dd9292 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/ElGamalTest.java @@ -0,0 +1,100 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.publickey; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class ElGamalTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/publickey/ElGamalTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + switch (findingId) { + case 0 -> { + // ElGamal.generate(2048, None) + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(PrivateKeyContext.class); + IValue v = detectionStore.getDetectionValues().get(0); + assertThat(v).isInstanceOf(KeySize.class); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(PrivateKey.class); + assertThat(root.getChildren()).hasSize(3); + assertThat(root.asString()).isEqualTo("ElGamal"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + assertThat(root.getChildren().get(PublicKeyEncryption.class)).isNotNull(); + assertThat(root.getChildren().get(KeyLength.class)).isNotNull(); + } + case 1 -> { + // ElGamal.construct((2,3,4)) + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyContext.class); + assertThat( + ((KeyAction) detectionStore.getDetectionValues().get(0)) + .getAction()) + .isEqualTo(KeyAction.Action.GENERATION); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(Key.class); + assertThat(root.getChildren()).hasSize(2); + assertThat(root.asString()).isEqualTo("ElGamal"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + assertThat(root.getChildren().get(PublicKeyEncryption.class)).isNotNull(); + assertThat(root.getChildren().get(PublicKeyEncryption.class).getChildren()) + .isEmpty(); + } + default -> throw new AssertionError("Unexpected findingId: " + findingId); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/RSATest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/RSATest.java new file mode 100644 index 000000000..b761bf335 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/RSATest.java @@ -0,0 +1,145 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.publickey; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class RSATest extends TestBase { + + public RSATest() { + super(PythonCryptoPublicKey.RSARules()); + } + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/publickey/RSATestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + switch (findingId) { + case 0 -> { + // RSA.generate(bits=2048) + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(PrivateKeyContext.class); + IValue kSz = detectionStore.getDetectionValues().get(0); + assertThat(kSz).isInstanceOf(KeySize.class); + assertThat(((KeySize) kSz).asString()).isEqualTo("2048"); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(PrivateKey.class); + assertThat(root.getChildren()).hasSize(3); + assertThat(root.asString()).isEqualTo("RSA"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + + INode pke = root.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + assertThat(pke.getChildren().get(Oid.class).asString()) + .isEqualTo("1.2.840.113549.1.1.1"); + + INode kgen = root.getChildren().get(KeyGeneration.class); + assertThat(kgen).isNotNull(); + + INode klen = root.getChildren().get(KeyLength.class); + assertThat(klen).isNotNull(); + assertThat(klen.asString()).isEqualTo("2048"); + } + case 1 -> { + // RSA.construct(...) + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyContext.class); + IValue kAction = detectionStore.getDetectionValues().get(0); + assertThat(kAction).isInstanceOf(KeyAction.class); + assertThat(((KeyAction) kAction).getAction()) + .isEqualTo(KeyAction.Action.GENERATION); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(Key.class); + assertThat(root.getChildren()).hasSize(2); + assertThat(root.asString()).isEqualTo("RSA"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + assertThat(root.getChildren().get(PublicKeyEncryption.class)).isNotNull(); + assertThat( + root.getChildren() + .get(PublicKeyEncryption.class) + .getChildren() + .get(Oid.class) + .asString()) + .isEqualTo("1.2.840.113549.1.1.1"); + } + case 2 -> { + // RSA.import_key(...) + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyContext.class); + assertThat( + ((KeyAction) detectionStore.getDetectionValues().get(0)) + .getAction()) + .isEqualTo(KeyAction.Action.GENERATION); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(Key.class); + assertThat(root.getChildren()).hasSize(2); + assertThat(root.asString()).isEqualTo("RSA"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + assertThat( + root.getChildren() + .get(PublicKeyEncryption.class) + .getChildren() + .get(Oid.class) + .asString()) + .isEqualTo("1.2.840.113549.1.1.1"); + } + default -> throw new AssertionError("Unexpected findingId: " + findingId); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/DSSSignTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/DSSSignTest.java new file mode 100644 index 000000000..1f63da7a4 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/DSSSignTest.java @@ -0,0 +1,128 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Sign; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class DSSSignTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/signature/DSSSignTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 2) { + // detection store + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(SignatureContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("DSS"); + + DetectionStore sigStore = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(sigStore).isNotNull(); + assertThat(sigStore.getDetectionValues()).hasSize(1); + assertThat(sigStore.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue sigValue = sigStore.getDetectionValues().get(0); + assertThat(sigValue).isInstanceOf(SignatureAction.class); + assertThat(sigValue.asString()).isEqualTo("SIGN"); + + // translation + assertThat(nodes).hasSize(1); + INode sig = nodes.get(0); + assertThat(sig.getKind()).isEqualTo(Signature.class); + assertThat(sig.getChildren()).hasSize(4); + assertThat(sig.asString()).isEqualTo("DSA-SHA-256"); + + INode oid = sig.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.3.2"); + + INode key = sig.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("DSA"); + assertThat(key.getChildren()).hasSize(2); + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.getChildren()).isEmpty(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + + INode md = sig.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.asString()).isEqualTo("SHA-256"); + assertThat(md.getChildren()).hasSize(4); + INode mdOid = md.getChildren().get(Oid.class); + assertThat(mdOid).isNotNull(); + assertThat(mdOid.getChildren()).isEmpty(); + assertThat(mdOid.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("256"); + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + + INode sign = sig.getChildren().get(Sign.class); + assertThat(sign).isNotNull(); + assertThat(sign.getChildren()).isEmpty(); + assertThat(sign.asString()).isEqualTo("SIGN"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/DSSVerifyTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/DSSVerifyTest.java new file mode 100644 index 000000000..f953bb88d --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/DSSVerifyTest.java @@ -0,0 +1,128 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Verify; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class DSSVerifyTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/signature/DSSVerifyTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 2) { + // detection store + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(SignatureContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("DSS"); + + DetectionStore sigStore = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(sigStore).isNotNull(); + assertThat(sigStore.getDetectionValues()).hasSize(1); + assertThat(sigStore.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue sigValue = sigStore.getDetectionValues().get(0); + assertThat(sigValue).isInstanceOf(SignatureAction.class); + assertThat(sigValue.asString()).isEqualTo("VERIFY"); + + // translation + assertThat(nodes).hasSize(1); + INode sig = nodes.get(0); + assertThat(sig.getKind()).isEqualTo(Signature.class); + assertThat(sig.getChildren()).hasSize(4); + assertThat(sig.asString()).isEqualTo("DSA-SHA-256"); + + INode oid = sig.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.3.2"); + + INode key = sig.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("DSA"); + assertThat(key.getChildren()).hasSize(2); + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.getChildren()).isEmpty(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + + INode md = sig.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.asString()).isEqualTo("SHA-256"); + assertThat(md.getChildren()).hasSize(4); + INode mdOid = md.getChildren().get(Oid.class); + assertThat(mdOid).isNotNull(); + assertThat(mdOid.getChildren()).isEmpty(); + assertThat(mdOid.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("256"); + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + + INode verify = sig.getChildren().get(Verify.class); + assertThat(verify).isNotNull(); + assertThat(verify.getChildren()).isEmpty(); + assertThat(verify.asString()).isEqualTo("VERIFY"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/ECDSASignTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/ECDSASignTest.java new file mode 100644 index 000000000..44c82d828 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/ECDSASignTest.java @@ -0,0 +1,132 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Sign; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class ECDSASignTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/signature/ECDSASignTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 2) { + // detection store + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(SignatureContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("ECDSA"); + + DetectionStore sigStore = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(sigStore).isNotNull(); + assertThat(sigStore.getDetectionValues()).hasSize(1); + assertThat(sigStore.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue sigValue = sigStore.getDetectionValues().get(0); + assertThat(sigValue).isInstanceOf(SignatureAction.class); + assertThat(sigValue.asString()).isEqualTo("SIGN"); + + // translation + assertThat(nodes).hasSize(1); + INode sig = nodes.get(0); + assertThat(sig.getKind()).isEqualTo(Signature.class); + assertThat(sig.getChildren()).hasSize(4); + assertThat(sig.asString()).isEqualTo("ECDSA-SHA-256"); + + INode oid = sig.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("1.2.840.10045.4.3.2"); + + INode key = sig.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("EC"); + assertThat(key.getChildren()).hasSize(2); + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.getChildren()).isEmpty(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + INode pke = key.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + assertThat(pke.getChildren().get(Oid.class).asString()).isEqualTo("1.2.840.10045.2.1"); + + INode md = sig.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.asString()).isEqualTo("SHA-256"); + assertThat(md.getChildren()).hasSize(4); + INode mdOid = md.getChildren().get(Oid.class); + assertThat(mdOid).isNotNull(); + assertThat(mdOid.getChildren()).isEmpty(); + assertThat(mdOid.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("256"); + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + + INode sign = sig.getChildren().get(Sign.class); + assertThat(sign).isNotNull(); + assertThat(sign.getChildren()).isEmpty(); + assertThat(sign.asString()).isEqualTo("SIGN"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/ECDSAVerifyTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/ECDSAVerifyTest.java new file mode 100644 index 000000000..e34b57645 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/ECDSAVerifyTest.java @@ -0,0 +1,132 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Verify; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class ECDSAVerifyTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/signature/ECDSAVerifyTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 2) { + // detection store + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(SignatureContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("ECDSA"); + + DetectionStore sigStore = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(sigStore).isNotNull(); + assertThat(sigStore.getDetectionValues()).hasSize(1); + assertThat(sigStore.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue sigValue = sigStore.getDetectionValues().get(0); + assertThat(sigValue).isInstanceOf(SignatureAction.class); + assertThat(sigValue.asString()).isEqualTo("VERIFY"); + + // translation + assertThat(nodes).hasSize(1); + INode sig = nodes.get(0); + assertThat(sig.getKind()).isEqualTo(Signature.class); + assertThat(sig.getChildren()).hasSize(4); + assertThat(sig.asString()).isEqualTo("ECDSA-SHA-256"); + + INode oid = sig.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("1.2.840.10045.4.3.2"); + + INode key = sig.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("EC"); + assertThat(key.getChildren()).hasSize(2); + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.getChildren()).isEmpty(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + INode pke = key.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + assertThat(pke.getChildren().get(Oid.class).asString()).isEqualTo("1.2.840.10045.2.1"); + + INode md = sig.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.asString()).isEqualTo("SHA-256"); + assertThat(md.getChildren()).hasSize(4); + INode mdOid = md.getChildren().get(Oid.class); + assertThat(mdOid).isNotNull(); + assertThat(mdOid.getChildren()).isEmpty(); + assertThat(mdOid.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("256"); + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + + INode verify = sig.getChildren().get(Verify.class); + assertThat(verify).isNotNull(); + assertThat(verify.getChildren()).isEmpty(); + assertThat(verify.asString()).isEqualTo("VERIFY"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/EdDSASignTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/EdDSASignTest.java new file mode 100644 index 000000000..8f4f6d267 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/EdDSASignTest.java @@ -0,0 +1,127 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Sign; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class EdDSASignTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/signature/EdDSASignTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 2) { + // detection store + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(SignatureContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("EDDSA"); + + DetectionStore sigStore = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(sigStore).isNotNull(); + assertThat(sigStore.getDetectionValues()).hasSize(1); + assertThat(sigStore.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue sigValue = sigStore.getDetectionValues().get(0); + assertThat(sigValue).isInstanceOf(SignatureAction.class); + assertThat(sigValue.asString()).isEqualTo("SIGN"); + + // translation + assertThat(nodes).hasSize(1); + INode sig = nodes.get(0); + assertThat(sig.getKind()).isEqualTo(Signature.class); + assertThat(sig.getChildren()).hasSize(3); + assertThat(sig.asString()).isEqualTo("EdDSA"); + + INode key = sig.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("EC"); + assertThat(key.getChildren()).hasSize(2); + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.getChildren()).isEmpty(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + INode pke = key.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + assertThat(pke.getChildren().get(Oid.class).asString()).isEqualTo("1.2.840.10045.2.1"); + + INode md = sig.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.asString()).isEqualTo("SHA-512"); + assertThat(md.getChildren()).hasSize(4); + INode mdOid = md.getChildren().get(Oid.class); + assertThat(mdOid).isNotNull(); + assertThat(mdOid.getChildren()).isEmpty(); + assertThat(mdOid.asString()).isEqualTo("2.16.840.1.101.3.4.2.3"); + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("512"); + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("1024"); + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + + INode sign = sig.getChildren().get(Sign.class); + assertThat(sign).isNotNull(); + assertThat(sign.getChildren()).isEmpty(); + assertThat(sign.asString()).isEqualTo("SIGN"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/EdDSAVerifyTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/EdDSAVerifyTest.java new file mode 100644 index 000000000..12582c85d --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/EdDSAVerifyTest.java @@ -0,0 +1,127 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Verify; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class EdDSAVerifyTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/signature/EdDSAVerifyTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 2) { + // detection store + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(SignatureContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("EDDSA"); + + DetectionStore sigStore = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(sigStore).isNotNull(); + assertThat(sigStore.getDetectionValues()).hasSize(1); + assertThat(sigStore.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue sigValue = sigStore.getDetectionValues().get(0); + assertThat(sigValue).isInstanceOf(SignatureAction.class); + assertThat(sigValue.asString()).isEqualTo("VERIFY"); + + // translation + assertThat(nodes).hasSize(1); + INode sig = nodes.get(0); + assertThat(sig.getKind()).isEqualTo(Signature.class); + assertThat(sig.getChildren()).hasSize(3); + assertThat(sig.asString()).isEqualTo("EdDSA"); + + INode key = sig.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("EC"); + assertThat(key.getChildren()).hasSize(2); + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.getChildren()).isEmpty(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + INode pke = key.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + assertThat(pke.getChildren().get(Oid.class).asString()).isEqualTo("1.2.840.10045.2.1"); + + INode md = sig.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.asString()).isEqualTo("SHA-512"); + assertThat(md.getChildren()).hasSize(4); + INode mdOid = md.getChildren().get(Oid.class); + assertThat(mdOid).isNotNull(); + assertThat(mdOid.getChildren()).isEmpty(); + assertThat(mdOid.asString()).isEqualTo("2.16.840.1.101.3.4.2.3"); + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("512"); + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("1024"); + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + + INode verify = sig.getChildren().get(Verify.class); + assertThat(verify).isNotNull(); + assertThat(verify.getChildren()).isEmpty(); + assertThat(verify.asString()).isEqualTo("VERIFY"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PKCS1v15SignTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PKCS1v15SignTest.java new file mode 100644 index 000000000..4fd919bd5 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PKCS1v15SignTest.java @@ -0,0 +1,141 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.Padding; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Sign; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class PKCS1v15SignTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/signature/PKCS1v15SignTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 2) { + // detection store + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(SignatureContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("RSA"); + + DetectionStore sigStore = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(sigStore).isNotNull(); + assertThat(sigStore.getDetectionValues()).hasSize(1); + assertThat(sigStore.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue sigValue = sigStore.getDetectionValues().get(0); + assertThat(sigValue).isInstanceOf(SignatureAction.class); + assertThat(sigValue.asString()).isEqualTo("SIGN"); + + // translation + assertThat(nodes).hasSize(1); + INode sig = nodes.get(0); + assertThat(sig.getKind()).isEqualTo(Signature.class); + assertThat(sig.getChildren()).hasSize(4); + assertThat(sig.asString()).isEqualTo("RSA-PKCS1-1.5-SHA-256"); + + INode oid = sig.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("1.2.840.113549.1.1.11"); + + INode padding = sig.getChildren().get(Padding.class); + assertThat(padding).isNotNull(); + assertThat(padding.getChildren()).isEmpty(); + assertThat(padding.asString()).isEqualTo("PKCS1"); + + INode key = sig.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("RSA"); + assertThat(key.getChildren()).hasSize(2); + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.getChildren()).isEmpty(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + INode pke = key.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + INode pkeOid = pke.getChildren().get(Oid.class); + assertThat(pkeOid).isNotNull(); + assertThat(pkeOid.getChildren()).isEmpty(); + assertThat(pkeOid.asString()).isEqualTo("1.2.840.113549.1.1.1"); + + INode md = sig.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.asString()).isEqualTo("SHA-256"); + assertThat(md.getChildren()).hasSize(4); + INode mdOid = md.getChildren().get(Oid.class); + assertThat(mdOid).isNotNull(); + assertThat(mdOid.getChildren()).isEmpty(); + assertThat(mdOid.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("256"); + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + + INode sign = sig.getChildren().get(Sign.class); + assertThat(sign).isNotNull(); + assertThat(sign.getChildren()).isEmpty(); + assertThat(sign.asString()).isEqualTo("SIGN"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PKCS1v15VerifyTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PKCS1v15VerifyTest.java new file mode 100644 index 000000000..d5486d323 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PKCS1v15VerifyTest.java @@ -0,0 +1,142 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.Padding; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Verify; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class PKCS1v15VerifyTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/signature/PKCS1v15VerifyTestFile.py", + this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 2) { + // detection store + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(SignatureContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("RSA"); + + DetectionStore sigStore = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(sigStore).isNotNull(); + assertThat(sigStore.getDetectionValues()).hasSize(1); + assertThat(sigStore.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue sigValue = sigStore.getDetectionValues().get(0); + assertThat(sigValue).isInstanceOf(SignatureAction.class); + assertThat(sigValue.asString()).isEqualTo("VERIFY"); + + // translation + assertThat(nodes).hasSize(1); + INode sig = nodes.get(0); + assertThat(sig.getKind()).isEqualTo(Signature.class); + assertThat(sig.getChildren()).hasSize(4); + assertThat(sig.asString()).isEqualTo("RSA-PKCS1-1.5-SHA-256"); + + INode oid = sig.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("1.2.840.113549.1.1.11"); + + INode padding = sig.getChildren().get(Padding.class); + assertThat(padding).isNotNull(); + assertThat(padding.getChildren()).isEmpty(); + assertThat(padding.asString()).isEqualTo("PKCS1"); + + INode key = sig.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("RSA"); + assertThat(key.getChildren()).hasSize(2); + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.getChildren()).isEmpty(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + INode pke = key.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + INode pkeOid = pke.getChildren().get(Oid.class); + assertThat(pkeOid).isNotNull(); + assertThat(pkeOid.getChildren()).isEmpty(); + assertThat(pkeOid.asString()).isEqualTo("1.2.840.113549.1.1.1"); + + INode md = sig.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.asString()).isEqualTo("SHA-256"); + assertThat(md.getChildren()).hasSize(4); + INode mdOid = md.getChildren().get(Oid.class); + assertThat(mdOid).isNotNull(); + assertThat(mdOid.getChildren()).isEmpty(); + assertThat(mdOid.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("256"); + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + + INode verify = sig.getChildren().get(Verify.class); + assertThat(verify).isNotNull(); + assertThat(verify.getChildren()).isEmpty(); + assertThat(verify.asString()).isEqualTo("VERIFY"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PSSSignTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PSSSignTest.java new file mode 100644 index 000000000..f87f57004 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PSSSignTest.java @@ -0,0 +1,135 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.ProbabilisticSignatureScheme; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Sign; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class PSSSignTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/signature/PSSSignTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 2) { + // detection store + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(SignatureContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("RSA-PSS"); + + DetectionStore sigStore = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(sigStore).isNotNull(); + assertThat(sigStore.getDetectionValues()).hasSize(1); + assertThat(sigStore.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue sigValue = sigStore.getDetectionValues().get(0); + assertThat(sigValue).isInstanceOf(SignatureAction.class); + assertThat(sigValue.asString()).isEqualTo("SIGN"); + + // translation + assertThat(nodes).hasSize(1); + INode pss = nodes.get(0); + assertThat(pss.getKind()).isEqualTo(ProbabilisticSignatureScheme.class); + assertThat(pss.getChildren()).hasSize(4); + assertThat(pss.asString()).isEqualTo("RSA-PSS"); + + INode oid = pss.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("1.2.840.113549.1.1.10"); + + INode key = pss.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("RSA"); + assertThat(key.getChildren()).hasSize(2); + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.getChildren()).isEmpty(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + INode pke = key.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + INode pkeOid = pke.getChildren().get(Oid.class); + assertThat(pkeOid).isNotNull(); + assertThat(pkeOid.getChildren()).isEmpty(); + assertThat(pkeOid.asString()).isEqualTo("1.2.840.113549.1.1.1"); + + INode md = pss.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.asString()).isEqualTo("SHA-256"); + assertThat(md.getChildren()).hasSize(4); + INode mdOid = md.getChildren().get(Oid.class); + assertThat(mdOid).isNotNull(); + assertThat(mdOid.getChildren()).isEmpty(); + assertThat(mdOid.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("256"); + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + + INode sign = pss.getChildren().get(Sign.class); + assertThat(sign).isNotNull(); + assertThat(sign.getChildren()).isEmpty(); + assertThat(sign.asString()).isEqualTo("SIGN"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PSSVerifyTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PSSVerifyTest.java new file mode 100644 index 000000000..6ada8cfb9 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PSSVerifyTest.java @@ -0,0 +1,135 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.ProbabilisticSignatureScheme; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Verify; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class PSSVerifyTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/signature/PSSVerifyTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 2) { + // detection store + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(SignatureContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("RSA-PSS"); + + DetectionStore sigStore = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(sigStore).isNotNull(); + assertThat(sigStore.getDetectionValues()).hasSize(1); + assertThat(sigStore.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue sigValue = sigStore.getDetectionValues().get(0); + assertThat(sigValue).isInstanceOf(SignatureAction.class); + assertThat(sigValue.asString()).isEqualTo("VERIFY"); + + // translation + assertThat(nodes).hasSize(1); + INode pss = nodes.get(0); + assertThat(pss.getKind()).isEqualTo(ProbabilisticSignatureScheme.class); + assertThat(pss.getChildren()).hasSize(4); + assertThat(pss.asString()).isEqualTo("RSA-PSS"); + + INode oid = pss.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("1.2.840.113549.1.1.10"); + + INode key = pss.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("RSA"); + assertThat(key.getChildren()).hasSize(2); + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.getChildren()).isEmpty(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + INode pke = key.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + INode pkeOid = pke.getChildren().get(Oid.class); + assertThat(pkeOid).isNotNull(); + assertThat(pkeOid.getChildren()).isEmpty(); + assertThat(pkeOid.asString()).isEqualTo("1.2.840.113549.1.1.1"); + + INode md = pss.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.asString()).isEqualTo("SHA-256"); + assertThat(md.getChildren()).hasSize(4); + INode mdOid = md.getChildren().get(Oid.class); + assertThat(mdOid).isNotNull(); + assertThat(mdOid.getChildren()).isEmpty(); + assertThat(mdOid.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("256"); + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + + INode verify = pss.getChildren().get(Verify.class); + assertThat(verify).isNotNull(); + assertThat(verify.getChildren()).isEmpty(); + assertThat(verify.asString()).isEqualTo("VERIFY"); + } + } +} From 1cdc23d3a994ef458c57f0cdc155c091e46acf68 Mon Sep 17 00:00:00 2001 From: san-zrl Date: Fri, 14 Aug 2026 12:38:58 +0200 Subject: [PATCH 10/13] docs: add PyCryptodome(x) to the supported-libraries table in README Signed-off-by: san-zrl --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4055c9a1b..73ff4f2fa 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ It is part of **the [CBOMKit](https://github.com/cbomkit) toolset**. | Java | [JCA](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html) | 100% | | | [BouncyCastle](https://github.com/bcgit/bc-java) (*light-weight API*) | 100%[^1] | | Python | [pyca/cryptography](https://cryptography.io/en/latest/) | 100% | +| | [PyCryptodome(x)](https://www.pycryptodome.org/) | 100%[^4] | | Go | [crypto](https://pkg.go.dev/crypto) (*standard library*) | 100%[^2] | | | [golang.org/x/crypto](https://pkg.go.dev/golang.org/x/crypto) | Partial[^3] | @@ -43,6 +44,7 @@ It is part of **the [CBOMKit](https://github.com/cbomkit) toolset**. [^1]: We only cover the BouncyCastle *light-weight API* according to [this specification](https://javadoc.io/static/org.bouncycastle/bctls-jdk14/1.80/specifications.html) [^2]: All packages under [`crypto`](https://pkg.go.dev/crypto@go1.25.6#section-directories) are covered except `crypto/x509` [^3]: Covers `golang.org/x/crypto/hkdf`, `golang.org/x/crypto/pbkdf2`, and `golang.org/x/crypto/sha3` +[^4]: Also covers the legacy PyCrypto library. > [!NOTE] > The plugin is designed in a modular way so that it can be extended to support additional languages and recognition rules to support more libraries. From 11d53bbeb3004a3b3fa502d55073b38279a966cb Mon Sep 17 00:00:00 2001 From: san-zrl Date: Fri, 14 Aug 2026 12:45:48 +0200 Subject: [PATCH 11/13] mapper: add PycaCurveMapper and PycaKeyBasedAlgorithmMapper; extend pyca mappers PycaCurveMapper: centralises curve-string to model mapping. Correctly splits Edwards curves (ED25519->Edwards25519, ED448->Edwards448) from Montgomery curves (CURVE25519->Curve25519, CURVE448->Curve448). Fixes SECP521R1 aliases (was SECP512R1) and re-adds the missing SECP256K1 case. PycaKeyBasedAlgorithmMapper: extracts the RSA/DSA/DH/ElGamal/Fernet algorithm switch that was duplicated across the three key-context translators. PycaCipherMapper: adds DES, RC2, Salsa20, AES128_GCM/AES256_GCM, RC4 alias, CHACHA20_POLY1305 alias. PycaDigestMapper/PycaMacMapper: adds MD2, MD4, RIPEMD-160, KMAC, TupleHash, cSHAKE, Keccak, KangarooTwelve for PyCryptodome rules. RIPEMD: adds (asKind, RIPEMD) copy constructor for MAC usage. KeyAgreementReorganizer: add REPLACE_ECDH_WITH_X25519_WHEN_CURVE25519 and REPLACE_ECDH_WITH_X448_WHEN_CURVE448 rules to convert a generic ECDH node into the correct XDH algorithm when both key children carry the matching curve. Signed-off-by: san-zrl --- .../rules/KeyAgreementReorganizer.java | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/mapper/src/main/java/com/ibm/mapper/reorganizer/rules/KeyAgreementReorganizer.java b/mapper/src/main/java/com/ibm/mapper/reorganizer/rules/KeyAgreementReorganizer.java index bd0b22086..c4fbbef87 100644 --- a/mapper/src/main/java/com/ibm/mapper/reorganizer/rules/KeyAgreementReorganizer.java +++ b/mapper/src/main/java/com/ibm/mapper/reorganizer/rules/KeyAgreementReorganizer.java @@ -19,14 +19,27 @@ */ package com.ibm.mapper.reorganizer.rules; +import com.ibm.mapper.model.Algorithm; import com.ibm.mapper.model.EllipticCurve; import com.ibm.mapper.model.INode; import com.ibm.mapper.model.KeyAgreement; +import com.ibm.mapper.model.Oid; import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.PublicKey; import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.algorithms.ECDH; +import com.ibm.mapper.model.algorithms.X25519; +import com.ibm.mapper.model.algorithms.X448; +import com.ibm.mapper.model.curves.Curve25519; +import com.ibm.mapper.model.curves.Curve448; import com.ibm.mapper.reorganizer.IReorganizerRule; import com.ibm.mapper.reorganizer.builder.ReorganizerRuleBuilder; +import com.ibm.mapper.utils.DetectionLocation; +import java.util.LinkedList; +import java.util.List; import java.util.Optional; +import java.util.function.Function; +import javax.annotation.Nonnull; public final class KeyAgreementReorganizer { @@ -55,4 +68,78 @@ private KeyAgreementReorganizer() { } return roots; }); + + public static final IReorganizerRule REPLACE_ECDH_WITH_X25519_WHEN_CURVE25519 = + replaceEcdhWithXdh(Curve25519.class, X25519::new); + + public static final IReorganizerRule REPLACE_ECDH_WITH_X448_WHEN_CURVE448 = + replaceEcdhWithXdh(Curve448.class, X448::new); + + /** + * Returns a rule that replaces an {@link ECDH} key-agreement root node with a specific XDH + * algorithm node when both its {@link PrivateKey} and {@link PublicKey} children carry the + * given {@code curveKind}. The replacement node is constructed via {@code xdhSupplier}, which + * is expected to set the correct canonical OID and curve; all other children ({@code + * KeyGeneration}, {@code PrivateKey}, {@code PublicKey}) are transferred from the old node. + * + * @param curveKind the {@link EllipticCurve} subclass to match on both key children + * @param xdhSupplier factory that builds the replacement node from a {@link + * com.ibm.mapper.utils.DetectionLocation} + */ + @Nonnull + public static IReorganizerRule replaceEcdhWithXdh( + @Nonnull Class curveKind, + @Nonnull Function xdhSupplier) { + return new ReorganizerRuleBuilder() + .createReorganizerRule("REPLACE_ECDH_WITH_XDH_WHEN_" + curveKind.getSimpleName()) + .forNodeKind(KeyAgreement.class) + .withDetectionCondition( + (node, parent, roots) -> + node instanceof ECDH + && hasCurveInKey(node, PrivateKey.class, curveKind) + && hasCurveInKey(node, PublicKey.class, curveKind)) + .perform( + (node, parent, roots) -> { + Algorithm xdh = xdhSupplier.apply(((ECDH) node).getDetectionContext()); + transferChildren(node, xdh); + return replaceRoot(roots, node, xdh); + }); + } + + // ── helpers ────────────────────────────────────────────────────────────── + + /** + * Returns {@code true} when {@code ecdh} has a {@code keyKind} child whose own {@code + * PublicKeyEncryption} child contains an {@link EllipticCurve} child that is an instance of + * {@code curveKind}. + */ + private static boolean hasCurveInKey( + @Nonnull INode ecdh, + @Nonnull Class keyKind, + @Nonnull Class curveKind) { + return ecdh.hasChildOfType(keyKind) + .flatMap(key -> key.hasChildOfType(PublicKeyEncryption.class)) + .flatMap(pke -> pke.hasChildOfType(EllipticCurve.class)) + .filter(curveKind::isInstance) + .isPresent(); + } + + /** + * Copies all children of {@code source} into {@code target}, skipping {@link Oid} so that the + * target's own canonical OID (set by its constructor) is preserved. + */ + private static void transferChildren(@Nonnull INode source, @Nonnull INode target) { + source.getChildren().entrySet().stream() + .filter(e -> !e.getKey().equals(Oid.class)) + .forEach(e -> target.put(e.getValue())); + } + + /** Returns a new roots list with {@code oldNode} replaced by {@code newNode}. */ + @Nonnull + private static List replaceRoot( + @Nonnull List roots, @Nonnull INode oldNode, @Nonnull INode newNode) { + List newRoots = new LinkedList<>(roots); + newRoots.replaceAll(r -> r == oldNode ? newNode : r); + return newRoots; + } } From 27e3b59b81b15706197390af4d181887727f3bf4 Mon Sep 17 00:00:00 2001 From: san-zrl Date: Fri, 14 Aug 2026 12:46:14 +0200 Subject: [PATCH 12/13] python/pyca: move pyca detection rules into detection/pyca/ sub-package Relocate all existing pyca rule classes (aead, asymmetric, fernet, hash, kdf, keyagreement, mac, padding, symmetric, wrapping) into the new detection/pyca/ sub-package to match the pycrypto/ layout and avoid future naming collisions. No logic changes. Signed-off-by: san-zrl --- .../plugin/rules/detection/aead/PycaAEAD.java | 95 ------- .../plugin/rules/detection/aead/PycaAES.java | 109 --------- .../rules/detection/asymmetric/PycaDSA.java | 114 --------- .../asymmetric/PycaDiffieHellman.java | 111 --------- .../asymmetric/PycaEllipticCurve.java | 159 ------------ .../rules/detection/asymmetric/PycaRSA.java | 204 ---------------- .../rules/detection/asymmetric/PycaSign.java | 77 ------ .../rules/detection/fernet/PycaFernet.java | 95 ------- .../plugin/rules/detection/hash/PycaHash.java | 122 --------- .../plugin/rules/detection/kdf/PycaKDF.java | 231 ------------------ .../keyagreement/PycaKeyAgreement.java | 76 ------ .../plugin/rules/detection/mac/PycaMAC.java | 91 ------- .../rules/detection/padding/PycaPadding.java | 103 -------- .../rules/detection/symmetric/PycaCipher.java | 133 ---------- .../detection/wrapping/PycaWrapping.java | 75 ------ 15 files changed, 1795 deletions(-) delete mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/aead/PycaAEAD.java delete mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/aead/PycaAES.java delete mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaDSA.java delete mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaDiffieHellman.java delete mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaEllipticCurve.java delete mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaRSA.java delete mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaSign.java delete mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/fernet/PycaFernet.java delete mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/hash/PycaHash.java delete mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/kdf/PycaKDF.java delete mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/keyagreement/PycaKeyAgreement.java delete mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/mac/PycaMAC.java delete mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/padding/PycaPadding.java delete mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/symmetric/PycaCipher.java delete mode 100644 python/src/main/java/com/ibm/plugin/rules/detection/wrapping/PycaWrapping.java diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/aead/PycaAEAD.java b/python/src/main/java/com/ibm/plugin/rules/detection/aead/PycaAEAD.java deleted file mode 100644 index 1b1a9812a..000000000 --- a/python/src/main/java/com/ibm/plugin/rules/detection/aead/PycaAEAD.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.aead; - -import com.ibm.engine.model.CipherAction; -import com.ibm.engine.model.KeyAction; -import com.ibm.engine.model.context.CipherContext; -import com.ibm.engine.model.context.SecretKeyContext; -import com.ibm.engine.model.factory.CipherActionFactory; -import com.ibm.engine.model.factory.KeyActionFactory; -import com.ibm.engine.rule.IDetectionRule; -import com.ibm.engine.rule.builder.DetectionRuleBuilder; -import com.ibm.plugin.rules.detection.Memoize; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; -import javax.annotation.Nonnull; -import org.sonar.plugins.python.api.tree.Tree; - -@SuppressWarnings("java:S1192") -public final class PycaAEAD { - - private PycaAEAD() { - // private - } - - private static final String TYPE = - "cryptography.hazmat.primitives.ciphers.aead.ChaCha20Poly1305"; - - private static final IDetectionRule ENCRYPT_CHACHA20POLY1305 = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(TYPE) - .forMethods("encrypt") - .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) - .withAnyParameters() - .buildForContext(new CipherContext(Map.of("algorithm", "ChaCha20Poly1305"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule DECRYPT_CHACHA20POLY1305 = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(TYPE) - .forMethods("decrypt") - .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) - .withAnyParameters() - .buildForContext(new CipherContext(Map.of("algorithm", "ChaCha20Poly1305"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule GENERATION_CHACHA20POLY1305 = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(TYPE) - .forMethods("generate_key") - .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) - .withAnyParameters() - .buildForContext( - new SecretKeyContext( - Map.of("algorithm", "ChaCha20Poly1305", "kind", "AEAD"))) - .inBundle(() -> "Pyca") - .withDependingDetectionRules( - List.of(ENCRYPT_CHACHA20POLY1305, DECRYPT_CHACHA20POLY1305)); - - private static final Supplier>> RULES = - Memoize.of(PycaAEAD::buildRules); - - @Nonnull - public static List> rules() { - return RULES.get(); - } - - @Nonnull - private static List> buildRules() { - return List.of(GENERATION_CHACHA20POLY1305); - } -} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/aead/PycaAES.java b/python/src/main/java/com/ibm/plugin/rules/detection/aead/PycaAES.java deleted file mode 100644 index 93d7dc0c5..000000000 --- a/python/src/main/java/com/ibm/plugin/rules/detection/aead/PycaAES.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.aead; - -import com.ibm.engine.model.CipherAction; -import com.ibm.engine.model.Size; -import com.ibm.engine.model.context.CipherContext; -import com.ibm.engine.model.context.SecretKeyContext; -import com.ibm.engine.model.factory.CipherActionFactory; -import com.ibm.engine.model.factory.KeySizeFactory; -import com.ibm.engine.rule.IDetectionRule; -import com.ibm.engine.rule.builder.DetectionRuleBuilder; -import com.ibm.plugin.rules.detection.Memoize; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; -import javax.annotation.Nonnull; -import org.sonar.plugins.python.api.tree.Tree; - -@SuppressWarnings("java:S1192") -public final class PycaAES { - - private PycaAES() { - // private - } - - private static final List aesAlgorithms = - Arrays.asList("AESGCM", "AESGCMIV", "AESOCB3", "AESSIV", "AESCCM"); - - private static final String AEAD_TYPE_PREFIX = "cryptography.hazmat.primitives.ciphers.aead."; - - private static @Nonnull IDetectionRule encryptAES(String aesAlgorithm) { - return new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(AEAD_TYPE_PREFIX + aesAlgorithm) - .forMethods("encrypt") - .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) - .withAnyParameters() - .buildForContext( - new CipherContext(Map.of("algorithm", aesAlgorithm, "kind", "AEAD"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - } - - private static @Nonnull IDetectionRule decryptAES(String aesAlgorithm) { - return new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(AEAD_TYPE_PREFIX + aesAlgorithm) - .forMethods("decrypt") - .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) - .withAnyParameters() - .buildForContext( - new CipherContext(Map.of("algorithm", aesAlgorithm, "kind", "AEAD"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - } - - private static @Nonnull List> generationRulesAES() { - LinkedList> rules = new LinkedList<>(); - for (String aesAlgorithm : aesAlgorithms) { - rules.add( - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(AEAD_TYPE_PREFIX + aesAlgorithm) - .forMethods("generate_key") - .withMethodParameter("int") - .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) - .buildForContext( - new SecretKeyContext( - Map.of("algorithm", aesAlgorithm, "kind", "AEAD"))) - .inBundle(() -> "Pyca") - .withDependingDetectionRules( - List.of(decryptAES(aesAlgorithm), encryptAES(aesAlgorithm)))); - } - return rules; - } - - private static final Supplier>> RULES = - Memoize.of(PycaAES::buildRules); - - @Nonnull - public static List> rules() { - return RULES.get(); - } - - @Nonnull - private static List> buildRules() { - return generationRulesAES(); - } -} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaDSA.java b/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaDSA.java deleted file mode 100644 index 51211987f..000000000 --- a/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaDSA.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.asymmetric; - -import static com.ibm.engine.detection.MethodMatcher.ANY; - -import com.ibm.engine.model.KeyAction; -import com.ibm.engine.model.SignatureAction; -import com.ibm.engine.model.Size; -import com.ibm.engine.model.context.PrivateKeyContext; -import com.ibm.engine.model.context.PublicKeyContext; -import com.ibm.engine.model.context.SignatureContext; -import com.ibm.engine.model.factory.KeyActionFactory; -import com.ibm.engine.model.factory.KeySizeFactory; -import com.ibm.engine.model.factory.SignatureActionFactory; -import com.ibm.engine.rule.IDetectionRule; -import com.ibm.engine.rule.builder.DetectionRuleBuilder; -import com.ibm.plugin.rules.detection.Memoize; -import com.ibm.plugin.rules.detection.hash.PycaHash; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; -import javax.annotation.Nonnull; -import org.sonar.plugins.python.api.tree.Tree; - -@SuppressWarnings("java:S1192") -public final class PycaDSA { - - private PycaDSA() { - // nothing - } - - private static final String TYPE = "cryptography.hazmat.primitives.asymmetric.dsa"; - - private static final IDetectionRule SIGN_DSA = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(TYPE + ".generate_private_key") - .forMethods("sign") - .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) - .withMethodParameter(ANY) - .withMethodParameter( - "cryptography.hazmat.primitives.*") // This "type" accepts both hashes - // and pre-hashes - .addDependingDetectionRules( - PycaHash.rules()) // The parameter of sign can either be an immediate - // hash, or a hash enclosed in the pre-hash - .buildForContext(new SignatureContext(Map.of("algorithm", "DSA"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule GENERATION_DSA = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(TYPE) - .forMethods("generate_private_key") - .withMethodParameter("int") - .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) - .buildForContext(new PrivateKeyContext(Map.of("algorithm", "DSA"))) - .inBundle(() -> "Pyca") - .withDependingDetectionRules(List.of(SIGN_DSA)); - - private static final IDetectionRule PUBLIC_NUMBERS_DSA = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(TYPE) - .forMethods("DSAPublicNumbers") - .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) - .withAnyParameters() - .buildForContext(new PublicKeyContext(Map.of("algorithm", "DSA"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule PRIVATE_NUMBERS_DSA = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(TYPE) - .forMethods("DSAPrivateNumbers") - .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) - .withAnyParameters() - .buildForContext(new PrivateKeyContext(Map.of("algorithm", "DSA"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final Supplier>> RULES = - Memoize.of(PycaDSA::buildRules); - - @Nonnull - public static List> rules() { - return RULES.get(); - } - - @Nonnull - private static List> buildRules() { - return List.of(GENERATION_DSA, PUBLIC_NUMBERS_DSA, PRIVATE_NUMBERS_DSA); - } -} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaDiffieHellman.java b/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaDiffieHellman.java deleted file mode 100644 index ae1fef88d..000000000 --- a/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaDiffieHellman.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.asymmetric; - -import static com.ibm.engine.detection.MethodMatcher.ANY; - -import com.ibm.engine.model.KeyAction; -import com.ibm.engine.model.Size; -import com.ibm.engine.model.context.PrivateKeyContext; -import com.ibm.engine.model.context.PublicKeyContext; -import com.ibm.engine.model.factory.KeyActionFactory; -import com.ibm.engine.model.factory.KeySizeFactory; -import com.ibm.engine.rule.IDetectionRule; -import com.ibm.engine.rule.builder.DetectionRuleBuilder; -import com.ibm.plugin.rules.detection.Memoize; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; -import javax.annotation.Nonnull; -import org.sonar.plugins.python.api.tree.Tree; - -@SuppressWarnings("java:S1192") -public final class PycaDiffieHellman { - - private PycaDiffieHellman() { - // private - } - - private static final String TYPE = "cryptography.hazmat.primitives.asymmetric.dh"; - - // The key size does not yet appear in PycaDiffieHellmanGenerateTestFile because - // of the TraceSymbol problem documented on the Github issue - private static final IDetectionRule GENERATE_PARAMETERS_DH = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(TYPE) - .forMethods("generate_parameters") - .withMethodParameter(ANY) - .withMethodParameter("int") - .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) - .buildForContext(new PrivateKeyContext(Map.of("algorithm", "DH"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule GENERATION_DH = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(TYPE + ".generate_parameters") - .forMethods("generate_private_key") - .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) - .withAnyParameters() - .buildForContext( - new PrivateKeyContext( - Map.of( - "algorithm", "DH", - "includePublicKey", "true"))) - .inBundle(() -> "Pyca") - .withDependingDetectionRules(List.of(GENERATE_PARAMETERS_DH)); - - private static final IDetectionRule PUBLIC_NUMBERS_DH = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(TYPE) - .forMethods("DHPublicNumbers") - .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) - .withAnyParameters() - .buildForContext(new PublicKeyContext(Map.of("algorithm", "DH"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule PRIVATE_NUMBERS_DH = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(TYPE) - .forMethods("DHPrivateNumbers") - .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) - .withAnyParameters() - .buildForContext(new PublicKeyContext(Map.of("algorithm", "DH"))) - .inBundle(() -> "CryptographyDiffieHellman") - .withoutDependingDetectionRules(); - - private static final Supplier>> RULES = - Memoize.of(PycaDiffieHellman::buildRules); - - @Nonnull - public static List> rules() { - return RULES.get(); - } - - @Nonnull - private static List> buildRules() { - return List.of(GENERATION_DH, PUBLIC_NUMBERS_DH, PRIVATE_NUMBERS_DH); - } -} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaEllipticCurve.java b/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaEllipticCurve.java deleted file mode 100644 index 195cc53b5..000000000 --- a/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaEllipticCurve.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.asymmetric; - -import static com.ibm.engine.detection.MethodMatcher.ANY; - -import com.ibm.engine.model.KeyAction; -import com.ibm.engine.model.SignatureAction; -import com.ibm.engine.model.context.KeyAgreementContext; -import com.ibm.engine.model.context.PrivateKeyContext; -import com.ibm.engine.model.context.PublicKeyContext; -import com.ibm.engine.model.context.SignatureContext; -import com.ibm.engine.model.factory.AlgorithmFactory; -import com.ibm.engine.model.factory.CurveFactory; -import com.ibm.engine.model.factory.KeyActionFactory; -import com.ibm.engine.model.factory.SignatureActionFactory; -import com.ibm.engine.rule.IDetectionRule; -import com.ibm.engine.rule.builder.DetectionRuleBuilder; -import com.ibm.plugin.rules.detection.Memoize; -import com.ibm.plugin.rules.detection.hash.PycaHash; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; -import javax.annotation.Nonnull; -import org.sonar.plugins.python.api.tree.Tree; - -@SuppressWarnings("java:S1192") -public final class PycaEllipticCurve { - - private PycaEllipticCurve() { - // private - } - - private static final String TYPE = "cryptography.hazmat.primitives.asymmetric.ec"; - private static final String GENERATE_METHOD = "generate_private_key"; - - // ECDSA is the only algorithm accepted as in the sign/verify functions (it is the only subclass - // of EllipticCurveSignatureAlgorithm) - private static final IDetectionRule ECDSA_EC = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(TYPE) - .forMethods("ECDSA") - .withMethodParameter( - "cryptography.hazmat.primitives.*") // This "type" accepts both hashes - // and pre-hashes - .addDependingDetectionRules( - PycaHash.rules()) // The parameter of ECDSA can either be an immediate - // hash, or a hash enclosed in the pre-hash function - .buildForContext(new SignatureContext(Map.of("algorithm", "ECDSA"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - public static final IDetectionRule KEY_EXCHANGE_EC = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(TYPE + "." + GENERATE_METHOD) - .forMethods("exchange") - .withMethodParameter(TYPE + ".*") - .shouldBeDetectedAs(new AlgorithmFactory<>()) - .withMethodParameter(ANY) - .buildForContext(new KeyAgreementContext(Map.of("algorithm", "EC"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule SIGN_EC = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(TYPE + "." + GENERATE_METHOD) - .forMethods("sign") - .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) - .withMethodParameter(ANY) - .withMethodParameter(TYPE + ".*") - .shouldBeDetectedAs(new AlgorithmFactory<>()) - .addDependingDetectionRules(List.of(ECDSA_EC)) - .buildForContext(new SignatureContext(Map.of("algorithm", "EC"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule GENERATION_EC = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(TYPE) - .forMethods(GENERATE_METHOD) - .withMethodParameter(ANY) - .shouldBeDetectedAs(new CurveFactory<>()) - .buildForContext(new PrivateKeyContext(Map.of("algorithm", "EC"))) - .inBundle(() -> "Pyca") - .withDependingDetectionRules(List.of(SIGN_EC, KEY_EXCHANGE_EC)); - - private static final IDetectionRule DERIVATION_EC = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(TYPE) - .forMethods("derive_private_key") - .withMethodParameter(ANY) - .withMethodParameter(ANY) - .shouldBeDetectedAs(new CurveFactory<>()) - .buildForContext(new PrivateKeyContext(Map.of("algorithm", "EC"))) - .inBundle(() -> "Pyca") - .withDependingDetectionRules(List.of(SIGN_EC, KEY_EXCHANGE_EC)); - - // Private numbers relies on information (the curve) given by the public key - // For now; we only use it as a depending detection rule of PRIVATE_NUMBERS_EC - private static final IDetectionRule PRIVATE_NUMBERS_EC = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(TYPE) - .forMethods("EllipticCurvePrivateNumbers") - .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) - .withMethodParameter(ANY) - .withMethodParameter(ANY) - .buildForContext(new PrivateKeyContext(Map.of("algorithm", "EC"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule PUBLIC_NUMBERS_EC = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(TYPE) - .forMethods("EllipticCurvePublicNumbers") - .withMethodParameter(ANY) - .withMethodParameter(ANY) - .withMethodParameter(ANY) - .shouldBeDetectedAs(new AlgorithmFactory<>()) - .buildForContext(new PublicKeyContext(Map.of("algorithm", "EC"))) - .inBundle(() -> "Pyca") - .withDependingDetectionRules(List.of(PRIVATE_NUMBERS_EC)); - - private static final Supplier>> RULES = - Memoize.of(PycaEllipticCurve::buildRules); - - @Nonnull - public static List> rules() { - return RULES.get(); - } - - @Nonnull - private static List> buildRules() { - return List.of(GENERATION_EC, DERIVATION_EC, PUBLIC_NUMBERS_EC); - } -} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaRSA.java b/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaRSA.java deleted file mode 100644 index 8eeaf5766..000000000 --- a/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaRSA.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.asymmetric; - -import static com.ibm.engine.detection.MethodMatcher.ANY; - -import com.ibm.engine.model.CipherAction; -import com.ibm.engine.model.KeyAction; -import com.ibm.engine.model.SignatureAction; -import com.ibm.engine.model.Size; -import com.ibm.engine.model.context.CipherContext; -import com.ibm.engine.model.context.PrivateKeyContext; -import com.ibm.engine.model.context.PublicKeyContext; -import com.ibm.engine.model.context.SignatureContext; -import com.ibm.engine.model.factory.CipherActionFactory; -import com.ibm.engine.model.factory.KeyActionFactory; -import com.ibm.engine.model.factory.KeySizeFactory; -import com.ibm.engine.model.factory.SignatureActionFactory; -import com.ibm.engine.model.factory.ValueActionFactory; -import com.ibm.engine.rule.IDetectionRule; -import com.ibm.engine.rule.builder.DetectionRuleBuilder; -import com.ibm.plugin.rules.detection.Memoize; -import com.ibm.plugin.rules.detection.hash.PycaHash; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; -import javax.annotation.Nonnull; -import org.sonar.plugins.python.api.tree.Tree; - -@SuppressWarnings("java:S1192") -public final class PycaRSA { - - private PycaRSA() { - // private - } - - private static final String PADDING_TYPE = "cryptography.hazmat.primitives.asymmetric.padding"; - private static final String HASH_TYPE = "cryptography.hazmat.primitives.*"; - private static final String RSA_TYPE = "cryptography.hazmat.primitives.asymmetric.rsa"; - - private static final IDetectionRule MGF1 = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(PADDING_TYPE) - .forMethods("MGF1") - .shouldBeDetectedAs(new ValueActionFactory<>("MGF1")) - .withMethodParameter(HASH_TYPE) // This "type" accepts both hashes - // and pre-hashes - .addDependingDetectionRules(PycaHash.rules()) - .buildForContext(new SignatureContext()) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule PSS = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(PADDING_TYPE) - .forMethods("PSS") - .shouldBeDetectedAs(new ValueActionFactory<>("RSA-PSS")) - .withMethodParameter(ANY) - .addDependingDetectionRules(List.of(MGF1)) - .withMethodParameter(ANY) - .buildForContext(new SignatureContext()) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule PKCS1v15 = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(PADDING_TYPE) - .forMethods("PKCS1v15") - .shouldBeDetectedAs( - new ValueActionFactory<>( - "PKCS1v15")) // this is necessary to capture something to - // trigger the translation - .withAnyParameters() - .buildForContext(new SignatureContext(Map.of("kind", "padding"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule OAEP = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(PADDING_TYPE) - .forMethods("OAEP") - .shouldBeDetectedAs(new ValueActionFactory<>("OAEP")) - .withMethodParameter(ANY) - // .shouldBeDetectedAs(new AlgorithmFactory<>()) - // .asChildOfParameterWithId(-1) - .addDependingDetectionRules(List.of(MGF1)) - .withMethodParameter(HASH_TYPE) // This "type" accepts both hashes - // and pre-hashes - .addDependingDetectionRules( - PycaHash.rules()) // The parameter of sign can either be an immediate - // hash, or a hash enclosed in the pre-hash - .withMethodParameter(ANY) - .buildForContext(new CipherContext(Map.of("kind", "padding"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule SIGN_RSA = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes( - "cryptography.hazmat.primitives.asymmetric.rsa.generate_private_key") - .forMethods("sign") - .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) - .withMethodParameter(ANY) - .withMethodParameter("cryptography.hazmat.primitives.asymmetric.padding.*") - .addDependingDetectionRules( - List.of( - PSS, - PKCS1v15)) // For signatures, padding can only be PSS or PKCSv15 - .withMethodParameter( - HASH_TYPE) // This "type" accepts both hashes and pre-hashes - .addDependingDetectionRules( - PycaHash.rules()) // The parameter of sign can either be an immediate - // hash, or a hash enclosed in the pre-hash - .buildForContext(new SignatureContext()) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule DECRYPT_RSA = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes( - "cryptography.hazmat.primitives.asymmetric.rsa.generate_private_key") - .forMethods("decrypt") - .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) - .withMethodParameter(ANY) - .withMethodParameter("cryptography.hazmat.primitives.asymmetric.padding.*") - .addDependingDetectionRules( - List.of( - OAEP, - PKCS1v15)) // For encryption/decryption, padding can only be - // OAEP or PKCSv15 - .buildForContext(new CipherContext(Map.of("algorithm", "RSA"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule GENERATION_RSA = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(RSA_TYPE) - .forMethods("generate_private_key") - .withMethodParameter(ANY) - .withMethodParameter("int") - .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) - .buildForContext(new PrivateKeyContext(Map.of("algorithm", "RSA"))) - .inBundle(() -> "Pyca") - .withDependingDetectionRules(List.of(SIGN_RSA /*,VERIFY_RSA*/, DECRYPT_RSA)); - - private static final IDetectionRule PUBLIC_NUMBERS_RSA = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(RSA_TYPE) - .forMethods("RSAPublicNumbers") - .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) - .withAnyParameters() - .buildForContext(new PublicKeyContext(Map.of("algorithm", "RSA"))) - .inBundle(() -> "Pyca") - .withDependingDetectionRules(List.of(SIGN_RSA /*, VERIFY_RSA*/, DECRYPT_RSA)); - - private static final IDetectionRule PRIVATE_NUMBERS_RSA = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(RSA_TYPE) - .forMethods("RSAPrivateNumbers") - .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) - .withAnyParameters() - .buildForContext(new PrivateKeyContext(Map.of("algorithm", "RSA"))) - .inBundle(() -> "Pyca") - .withDependingDetectionRules(List.of(SIGN_RSA /*, VERIFY_RSA*/, DECRYPT_RSA)); - - private static final Supplier>> RULES = - Memoize.of(PycaRSA::buildRules); - - @Nonnull - public static List> rules() { - return RULES.get(); - } - - @Nonnull - private static List> buildRules() { - return List.of(GENERATION_RSA, PUBLIC_NUMBERS_RSA, PRIVATE_NUMBERS_RSA); - } -} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaSign.java b/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaSign.java deleted file mode 100644 index 6b50bf24a..000000000 --- a/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaSign.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.asymmetric; - -import com.ibm.engine.model.KeyAction; -import com.ibm.engine.model.context.PrivateKeyContext; -import com.ibm.engine.model.factory.KeyActionFactory; -import com.ibm.engine.rule.IDetectionRule; -import com.ibm.engine.rule.builder.DetectionRuleBuilder; -import com.ibm.plugin.rules.detection.Memoize; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; -import javax.annotation.Nonnull; -import org.sonar.plugins.python.api.tree.Tree; - -@SuppressWarnings("java:S1192") -public final class PycaSign { - - private PycaSign() { - // private - } - - private static final IDetectionRule SIGN_ED25519 = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes( - "cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey") - .forMethods("generate") - .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) - .withoutParameters() - .buildForContext(new PrivateKeyContext(Map.of("algorithm", "Ed25519"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule SIGN_ED448 = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes( - "cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey") - .forMethods("generate") - .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) - .withoutParameters() - .buildForContext(new PrivateKeyContext(Map.of("algorithm", "Ed448"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final Supplier>> RULES = - Memoize.of(PycaSign::buildRules); - - @Nonnull - public static List> rules() { - return RULES.get(); - } - - @Nonnull - private static List> buildRules() { - return List.of(SIGN_ED25519, SIGN_ED448); - } -} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/fernet/PycaFernet.java b/python/src/main/java/com/ibm/plugin/rules/detection/fernet/PycaFernet.java deleted file mode 100644 index a1eb299f9..000000000 --- a/python/src/main/java/com/ibm/plugin/rules/detection/fernet/PycaFernet.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.fernet; - -import com.ibm.engine.model.CipherAction; -import com.ibm.engine.model.KeyAction; -import com.ibm.engine.model.context.CipherContext; -import com.ibm.engine.model.context.KeyContext; -import com.ibm.engine.model.factory.CipherActionFactory; -import com.ibm.engine.model.factory.KeyActionFactory; -import com.ibm.engine.rule.IDetectionRule; -import com.ibm.engine.rule.builder.DetectionRuleBuilder; -import com.ibm.plugin.rules.detection.Memoize; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; -import javax.annotation.Nonnull; -import org.sonar.plugins.python.api.tree.Tree; - -@SuppressWarnings("java:S1192") -public final class PycaFernet { - - private PycaFernet() { - // private - } - - private static @Nonnull List> encryptDecryptFernet() { - List methodNames = - List.of("encrypt", "encrypt_at_time", "decrypt", "decrypt_at_time"); - List objectNames = List.of("Fernet", "MultiFernet"); - List> rules = new LinkedList<>(); - - for (String method : methodNames) { - for (String object : objectNames) { - rules.add( - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes("cryptography.fernet." + object) - .forMethods(method) - .shouldBeDetectedAs( - new CipherActionFactory<>( - method.startsWith("encrypt") - ? CipherAction.Action.ENCRYPT - : CipherAction.Action.DECRYPT)) - .withAnyParameters() - .buildForContext(new CipherContext(Map.of("algorithm", "Fernet"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules()); - } - } - return rules; - } - - private static final IDetectionRule GENERATION_FERNET = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes("cryptography.fernet.Fernet") - .forMethods("generate_key") - .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) - .withAnyParameters() - .buildForContext(new KeyContext(Map.of("algorithm", "Fernet"))) - .inBundle(() -> "Pyca") - .withDependingDetectionRules(encryptDecryptFernet()); - - private static final Supplier>> RULES = - Memoize.of(PycaFernet::buildRules); - - @Nonnull - public static List> rules() { - return RULES.get(); - } - - @Nonnull - private static List> buildRules() { - return List.of(GENERATION_FERNET); - } -} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/hash/PycaHash.java b/python/src/main/java/com/ibm/plugin/rules/detection/hash/PycaHash.java deleted file mode 100644 index 27aead4c7..000000000 --- a/python/src/main/java/com/ibm/plugin/rules/detection/hash/PycaHash.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.hash; - -import com.ibm.engine.model.context.DigestContext; -import com.ibm.engine.model.factory.AlgorithmFactory; -import com.ibm.engine.model.factory.ValueActionFactory; -import com.ibm.engine.rule.IDetectionRule; -import com.ibm.engine.rule.builder.DetectionRuleBuilder; -import com.ibm.plugin.rules.detection.Memoize; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.function.Supplier; -import javax.annotation.Nonnull; -import org.sonar.plugins.python.api.tree.Tree; - -@SuppressWarnings("java:S1192") -public final class PycaHash { - - private PycaHash() { - // private - } - - @SuppressWarnings("java:S2386") - public static final List hashes = - Arrays.asList( - "SHA1", - "SHA512_224", - "SHA512_256", - "SHA224", - "SHA256", - "SHA384", - "SHA512", - "SHA3_224", - "SHA3_256", - "SHA3_384", - "SHA3_512", - "SHAKE128", - "SHAKE256", - "MD5", - "BLAKE2b", - "BLAKE2s", - "SM3"); - - private static @Nonnull List> hashesRules() { - LinkedList> rules = new LinkedList<>(); - for (final String hash : PycaHash.hashes) { - rules.add( - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes("cryptography.hazmat.primitives.hashes") - .forMethods(hash) - .shouldBeDetectedAs(new ValueActionFactory<>(hash)) - .withAnyParameters() - .buildForContext(new DigestContext()) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules()); - } - return rules; - } - - private static final IDetectionRule PRE_HASH = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes("cryptography.hazmat.primitives.asymmetric.utils") - .forMethods("Prehashed") - .withMethodParameter("cryptography.hazmat.primitives.hashes.*") - .addDependingDetectionRules(hashesRules()) - .buildForContext(new DigestContext()) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - // Detects hashes.Hash(hashes.SHA256()) and similar direct hash-computation usages. - private static final IDetectionRule HASH_WRAPPER = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes("cryptography.hazmat.primitives.hashes") - .forMethods("Hash") - .withMethodParameter("cryptography.hazmat.primitives.hashes.*") - .shouldBeDetectedAs(new AlgorithmFactory<>()) - .buildForContext(new DigestContext()) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final Supplier>> RULES = - Memoize.of(PycaHash::buildRules); - - @Nonnull - public static List> rules() { - return RULES.get(); - } - - @Nonnull - private static List> buildRules() { - final List> hashAndPrehashRules = new LinkedList<>(hashesRules()); - hashAndPrehashRules.add(PRE_HASH); - return hashAndPrehashRules; - } - - @Nonnull - public static List> wrapperRules() { - return List.of(HASH_WRAPPER); - } -} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/kdf/PycaKDF.java b/python/src/main/java/com/ibm/plugin/rules/detection/kdf/PycaKDF.java deleted file mode 100644 index 2c2dc8a3c..000000000 --- a/python/src/main/java/com/ibm/plugin/rules/detection/kdf/PycaKDF.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.kdf; - -import static com.ibm.engine.detection.MethodMatcher.ANY; - -import com.ibm.engine.model.AlgorithmParameter; -import com.ibm.engine.model.Size; -import com.ibm.engine.model.context.KeyDerivationFunctionContext; -import com.ibm.engine.model.factory.AlgorithmFactory; -import com.ibm.engine.model.factory.AlgorithmParameterFactory; -import com.ibm.engine.model.factory.KeySizeFactory; -import com.ibm.engine.model.factory.ModeFactory; -import com.ibm.engine.model.factory.ValueActionFactory; -import com.ibm.engine.rule.IDetectionRule; -import com.ibm.engine.rule.builder.DetectionRuleBuilder; -import com.ibm.plugin.rules.detection.Memoize; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; -import javax.annotation.Nonnull; -import org.sonar.plugins.python.api.tree.Tree; - -@SuppressWarnings("java:S1192") -public final class PycaKDF { - - private PycaKDF() { - // private - } - - private static final String HASH_TYPE = "cryptography.hazmat.primitives.hashes.*"; - private static final String KDF_TYPE_PREFIX = "cryptography.hazmat.primitives.kdf."; - - private static final IDetectionRule X963KDF = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(KDF_TYPE_PREFIX + "x963kdf") - .forMethods("X963KDF") - .withMethodParameter(HASH_TYPE) // Accepts only hashes (not pre-hashes) - .shouldBeDetectedAs(new AlgorithmFactory<>()) - .withMethodParameter("int") - .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) - .asChildOfParameterWithId(0) - .withMethodParameter(ANY) - .buildForContext(new KeyDerivationFunctionContext(Map.of("kind", "x963"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule KBKDFCMAC = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(KDF_TYPE_PREFIX + "kbkdf") - .forMethods("KBKDFCMAC") - .withMethodParameter("cryptography.hazmat.primitives.ciphers.algorithms.*") - .shouldBeDetectedAs(new AlgorithmFactory<>()) - .withMethodParameter(ANY) - .shouldBeDetectedAs(new ModeFactory<>()) - .asChildOfParameterWithId(0) - .withMethodParameter("int") - .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) - .asChildOfParameterWithId(0) - .withMethodParameter(ANY) - .withMethodParameter(ANY) - .withMethodParameter(ANY) - .withMethodParameter(ANY) - .withMethodParameter(ANY) - .withMethodParameter(ANY) - .buildForContext(new KeyDerivationFunctionContext(Map.of("kind", "cmac"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule KBKDFHMAC = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(KDF_TYPE_PREFIX + "kbkdf") - .forMethods("KBKDFHMAC") - .withMethodParameter(HASH_TYPE) // Accepts only hashes (not pre-hashes) - .shouldBeDetectedAs(new AlgorithmFactory<>()) - .withMethodParameter(ANY) - .shouldBeDetectedAs(new ModeFactory<>()) - .asChildOfParameterWithId(0) - .withMethodParameter("int") - .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) - .asChildOfParameterWithId(0) - .withMethodParameter(ANY) - .withMethodParameter(ANY) - .withMethodParameter(ANY) - .withMethodParameter(ANY) - .withMethodParameter(ANY) - .withMethodParameter(ANY) - .buildForContext(new KeyDerivationFunctionContext(Map.of("kind", "hmac"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule HKDF_EXPAND = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(KDF_TYPE_PREFIX + "hkdf") - .forMethods("HKDFExpand") - .withMethodParameter(HASH_TYPE) // Accepts only hashes (not pre-hashes) - .shouldBeDetectedAs(new AlgorithmFactory<>()) - .withMethodParameter("int") - .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) - .asChildOfParameterWithId(0) - .withMethodParameter(ANY) - .buildForContext(new KeyDerivationFunctionContext(Map.of("kind", "hkdf"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule HKDF = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(KDF_TYPE_PREFIX + "hkdf") - .forMethods("HKDF") - .withMethodParameter(HASH_TYPE) // Accepts only hashes (not pre-hashes) - .shouldBeDetectedAs(new AlgorithmFactory<>()) - .withMethodParameter("int") - .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) - .asChildOfParameterWithId(0) - .withMethodParameter(ANY) - .withMethodParameter(ANY) - .buildForContext(new KeyDerivationFunctionContext(Map.of("kind", "hkdf"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule CONCAT_KDF_HMAC = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(KDF_TYPE_PREFIX + "concatkdf") - .forMethods("ConcatKDFHMAC") - .withMethodParameter(HASH_TYPE) // Accepts only hashes (not pre-hashes) - .shouldBeDetectedAs(new AlgorithmFactory<>()) - .withMethodParameter("int") - .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) - .asChildOfParameterWithId(0) - .withMethodParameter(ANY) - .withMethodParameter(ANY) - .buildForContext(new KeyDerivationFunctionContext(Map.of("kind", "concatkdf"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule CONCAT_KDF = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(KDF_TYPE_PREFIX + "concatkdf") - .forMethods("ConcatKDFHash") - .withMethodParameter(HASH_TYPE) // Accepts only hashes (not pre-hashes) - .shouldBeDetectedAs(new AlgorithmFactory<>()) - .withMethodParameter("int") - .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) - .asChildOfParameterWithId(0) - .withMethodParameter(ANY) - .buildForContext(new KeyDerivationFunctionContext(Map.of("kind", "concatkdf"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule SCRYPT = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(KDF_TYPE_PREFIX + "scrypt") - .forMethods("Scrypt") - .shouldBeDetectedAs(new ValueActionFactory<>("Scrypt")) - .withMethodParameter(ANY) - .withMethodParameter("int") - .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) - .asChildOfParameterWithId(0) - .withMethodParameter("int") - .withMethodParameter("int") - .withMethodParameter("int") - .buildForContext(new KeyDerivationFunctionContext()) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule PBKDF2 = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(KDF_TYPE_PREFIX + "pbkdf2") - .forMethods("PBKDF2HMAC") - .withMethodParameter(HASH_TYPE) // Accepts only hashes (not pre-hashes) - .shouldBeDetectedAs(new AlgorithmFactory<>()) - .withMethodParameter("int") - .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) - .asChildOfParameterWithId(0) - .withMethodParameter(ANY) - .withMethodParameter("int") - .shouldBeDetectedAs( - new AlgorithmParameterFactory<>(AlgorithmParameter.Kind.ITERATIONS)) - .asChildOfParameterWithId(0) - .buildForContext(new KeyDerivationFunctionContext(Map.of("kind", "pbkdf2"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final Supplier>> RULES = - Memoize.of(PycaKDF::buildRules); - - @Nonnull - public static List> rules() { - return RULES.get(); - } - - @Nonnull - private static List> buildRules() { - return List.of( - PBKDF2, - SCRYPT, - CONCAT_KDF, - CONCAT_KDF_HMAC, - HKDF, - HKDF_EXPAND, - KBKDFHMAC, - KBKDFCMAC, - X963KDF); - } -} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/keyagreement/PycaKeyAgreement.java b/python/src/main/java/com/ibm/plugin/rules/detection/keyagreement/PycaKeyAgreement.java deleted file mode 100644 index 127c0bca2..000000000 --- a/python/src/main/java/com/ibm/plugin/rules/detection/keyagreement/PycaKeyAgreement.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.keyagreement; - -import com.ibm.engine.model.KeyAction; -import com.ibm.engine.model.context.KeyAgreementContext; -import com.ibm.engine.model.factory.KeyActionFactory; -import com.ibm.engine.rule.IDetectionRule; -import com.ibm.engine.rule.builder.DetectionRuleBuilder; -import com.ibm.plugin.rules.detection.Memoize; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; -import javax.annotation.Nonnull; -import org.sonar.plugins.python.api.tree.Tree; - -@SuppressWarnings("java:S1192") -public final class PycaKeyAgreement { - - private PycaKeyAgreement() { - // private - } - - private static final IDetectionRule GENERATION_X25519 = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes( - "cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey") - .forMethods("generate") - .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) - .withoutParameters() - .buildForContext(new KeyAgreementContext(Map.of("algorithm", "x25519"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule GENERATION_X448 = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes("cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey") - .forMethods("generate") - .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) - .withoutParameters() - .buildForContext(new KeyAgreementContext(Map.of("algorithm", "x448"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final Supplier>> RULES = - Memoize.of(PycaKeyAgreement::buildRules); - - @Nonnull - public static List> rules() { - return RULES.get(); - } - - @Nonnull - private static List> buildRules() { - return List.of(GENERATION_X25519, GENERATION_X448); - } -} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/mac/PycaMAC.java b/python/src/main/java/com/ibm/plugin/rules/detection/mac/PycaMAC.java deleted file mode 100644 index 4cbedf859..000000000 --- a/python/src/main/java/com/ibm/plugin/rules/detection/mac/PycaMAC.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.mac; - -import static com.ibm.engine.detection.MethodMatcher.ANY; - -import com.ibm.engine.model.context.MacContext; -import com.ibm.engine.model.factory.AlgorithmFactory; -import com.ibm.engine.model.factory.ValueActionFactory; -import com.ibm.engine.rule.IDetectionRule; -import com.ibm.engine.rule.builder.DetectionRuleBuilder; -import com.ibm.plugin.rules.detection.Memoize; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; -import javax.annotation.Nonnull; -import org.sonar.plugins.python.api.tree.Tree; - -@SuppressWarnings("java:S1192") -public final class PycaMAC { - - private PycaMAC() { - // private - } - - private static final IDetectionRule NEW_CMAC = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes("cryptography.hazmat.primitives.cmac") - .forMethods("CMAC") - .withMethodParameter("cryptography.hazmat.primitives.ciphers.algorithms.*") - .shouldBeDetectedAs(new AlgorithmFactory<>()) - .buildForContext(new MacContext(Map.of("kind", "cmac"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule NEW_HMAC = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes("cryptography.hazmat.primitives.hmac") - .forMethods("HMAC") - .withMethodParameter(ANY) - .withMethodParameter( - "cryptography.hazmat.primitives.hashes.*") // Accepts only hashes (not - // pre-hashes) - .shouldBeDetectedAs(new AlgorithmFactory<>()) - .buildForContext(new MacContext(Map.of("kind", "hmac"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule NEW_POLY1305 = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes("cryptography.hazmat.primitives.poly1305") - .forMethods("Poly1305") - .shouldBeDetectedAs(new ValueActionFactory<>("Poly1305")) - .withAnyParameters() - .buildForContext(new MacContext()) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final Supplier>> RULES = - Memoize.of(PycaMAC::buildRules); - - @Nonnull - public static List> rules() { - return RULES.get(); - } - - @Nonnull - private static List> buildRules() { - return List.of(NEW_CMAC, NEW_HMAC, NEW_POLY1305); - } -} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/padding/PycaPadding.java b/python/src/main/java/com/ibm/plugin/rules/detection/padding/PycaPadding.java deleted file mode 100644 index 404367ff3..000000000 --- a/python/src/main/java/com/ibm/plugin/rules/detection/padding/PycaPadding.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.padding; - -import com.ibm.engine.model.Size; -import com.ibm.engine.model.context.CipherContext; -import com.ibm.engine.model.factory.BlockSizeFactory; -import com.ibm.engine.model.factory.ValueActionFactory; -import com.ibm.engine.rule.IDetectionRule; -import com.ibm.engine.rule.builder.DetectionRuleBuilder; -import com.ibm.plugin.rules.detection.Memoize; -import com.ibm.plugin.rules.detection.symmetric.PycaCipher; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; -import javax.annotation.Nonnull; -import org.sonar.plugins.python.api.tree.Tree; - -@SuppressWarnings("java:S1192") -public final class PycaPadding { - - private PycaPadding() { - // private - } - - private static final List paddings = Arrays.asList("PKCS7", "ANSIX923"); - - private static @Nonnull List> newPadding() { - final LinkedList> rules = new LinkedList<>(); - - for (String padding : paddings) { - rules.add( - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes("cryptography.hazmat.primitives.padding") - .forMethods(padding) - .shouldBeDetectedAs(new ValueActionFactory<>(padding)) - .withMethodParameter("int") - .shouldBeDetectedAs(new BlockSizeFactory<>(Size.UnitType.BIT)) - .asChildOfParameterWithId(0) - .buildForContext(new CipherContext(Map.of("kind", "padding"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules()); - } - // When the block size is specified using a `block_size` attribute - for (String padding : paddings) { - for (String cipherAlgorithm : PycaCipher.blockCiphers) { - rules.add( - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes("cryptography.hazmat.primitives.padding") - .forMethods(padding) - .shouldBeDetectedAs(new ValueActionFactory<>(padding)) - .withMethodParameter( - "cryptography.hazmat.primitives.ciphers.algorithms." - + cipherAlgorithm - + ".block_size") - .shouldBeDetectedAs(new BlockSizeFactory<>(Size.UnitType.BIT)) - .asChildOfParameterWithId(0) - .buildForContext(new CipherContext(Map.of("kind", "padding"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules()); - } - } - return rules; - } - - // It should be better to only detect Padding when it actually gets implied (i.e. there is - // `padder.update` function call). However, it does not bring much, and creates problems - // because the type handler may not distinguish an `encryptor.update` from `padder.update`. - - private static final Supplier>> RULES = - Memoize.of(PycaPadding::buildRules); - - @Nonnull - public static List> rules() { - return RULES.get(); - } - - @Nonnull - private static List> buildRules() { - return newPadding(); - } -} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/symmetric/PycaCipher.java b/python/src/main/java/com/ibm/plugin/rules/detection/symmetric/PycaCipher.java deleted file mode 100644 index 2438d9eaf..000000000 --- a/python/src/main/java/com/ibm/plugin/rules/detection/symmetric/PycaCipher.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.symmetric; - -import com.ibm.engine.model.CipherAction; -import com.ibm.engine.model.context.CipherContext; -import com.ibm.engine.model.factory.AlgorithmFactory; -import com.ibm.engine.model.factory.CipherActionFactory; -import com.ibm.engine.model.factory.ModeFactory; -import com.ibm.engine.rule.IDetectionRule; -import com.ibm.engine.rule.builder.DetectionRuleBuilder; -import com.ibm.plugin.rules.detection.Memoize; -import com.ibm.plugin.rules.detection.padding.PycaPadding; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.function.Supplier; -import javax.annotation.Nonnull; -import org.sonar.plugins.python.api.tree.Tree; - -@SuppressWarnings({"java:S2386", "java:S1192"}) -public final class PycaCipher { - - private PycaCipher() { - // private - } - - public static final List blockCiphers = - Arrays.asList( - "AES", - "AES128", - "AES256", - "Camellia", - "TripleDES", - "CAST5", - "SEED", - "SM4", - "Blowfish", - "IDEA"); - public static final List streamCiphers = Arrays.asList("ChaCha20", "ARC4"); - - public static final List modes = - Arrays.asList("CBC", "CTR", "OFB", "CFB", "CFB8", "GCM", "XTS", "ECB"); - - private static final IDetectionRule ENCRYPT_CIPHER = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes("cryptography.hazmat.primitives.ciphers.Cipher") - .forMethods("encryptor") - .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) - .withAnyParameters() - .buildForContext(new CipherContext()) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule DECRYPT_CIPHER = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes("cryptography.hazmat.primitives.ciphers.Cipher") - .forMethods("decryptor") - .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) - .withAnyParameters() - .buildForContext(new CipherContext()) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static @Nonnull List> followingNewCipherRules() { - final List> encryptionRules = - new LinkedList<>(List.of(DECRYPT_CIPHER, ENCRYPT_CIPHER)); - encryptionRules.addAll(PycaPadding.rules()); - return encryptionRules; - } - - private static final IDetectionRule NEW_CIPHER = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes("cryptography.hazmat.primitives.ciphers") - .forMethods("Cipher") - .withMethodParameter("cryptography.hazmat.primitives.ciphers.algorithms.*") - .shouldBeDetectedAs(new AlgorithmFactory<>()) - .addDependingDetectionRules(followingNewCipherRules()) - .withMethodParameter("cryptography.hazmat.primitives.ciphers.modes.*") - .shouldBeDetectedAs(new ModeFactory<>()) - .asChildOfParameterWithId(0) - .buildForContext(new CipherContext()) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule STREAM_CIPHER = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes("cryptography.hazmat.primitives.ciphers") - .forMethods("Cipher") - .withMethodParameter("cryptography.hazmat.primitives.ciphers.algorithms.*") - .shouldBeDetectedAs(new AlgorithmFactory<>()) - .addDependingDetectionRules(followingNewCipherRules()) - .withMethodParameter("None") - .shouldBeDetectedAs(new ModeFactory<>()) - .asChildOfParameterWithId(0) - .buildForContext(new CipherContext()) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final Supplier>> RULES = - Memoize.of(PycaCipher::buildRules); - - @Nonnull - public static List> rules() { - return RULES.get(); - } - - @Nonnull - private static List> buildRules() { - return List.of(NEW_CIPHER, STREAM_CIPHER); - } -} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/wrapping/PycaWrapping.java b/python/src/main/java/com/ibm/plugin/rules/detection/wrapping/PycaWrapping.java deleted file mode 100644 index 7ffd52972..000000000 --- a/python/src/main/java/com/ibm/plugin/rules/detection/wrapping/PycaWrapping.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.wrapping; - -import com.ibm.engine.model.CipherAction; -import com.ibm.engine.model.context.CipherContext; -import com.ibm.engine.model.factory.CipherActionFactory; -import com.ibm.engine.rule.IDetectionRule; -import com.ibm.engine.rule.builder.DetectionRuleBuilder; -import com.ibm.plugin.rules.detection.Memoize; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; -import javax.annotation.Nonnull; -import org.sonar.plugins.python.api.tree.Tree; - -@SuppressWarnings("java:S1192") -public final class PycaWrapping { - - private PycaWrapping() { - // private - } - - private static final IDetectionRule AES_KEY_WRAP = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes("cryptography.hazmat.primitives.keywrap") - .forMethods("aes_key_wrap") - .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.WRAP)) - .withAnyParameters() - .buildForContext(new CipherContext(Map.of("algorithm", "AES"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final IDetectionRule AES_KEY_WRAP_WITH_PADDING = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes("cryptography.hazmat.primitives.keywrap") - .forMethods("aes_key_wrap_with_padding") - .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.WRAP)) - .withAnyParameters() - .buildForContext(new CipherContext(Map.of("algorithm", "AES"))) - .inBundle(() -> "Pyca") - .withoutDependingDetectionRules(); - - private static final Supplier>> RULES = - Memoize.of(PycaWrapping::buildRules); - - @Nonnull - public static List> rules() { - return RULES.get(); - } - - @Nonnull - private static List> buildRules() { - return List.of(AES_KEY_WRAP, AES_KEY_WRAP_WITH_PADDING); - } -} From 87c52841119e34a21e333f5da7e1762b8edb45b1 Mon Sep 17 00:00:00 2001 From: san-zrl Date: Fri, 14 Aug 2026 12:55:03 +0200 Subject: [PATCH 13/13] python/pyca: remove old pre-reorganisation source locations Delete the original flat detection/{aead,asymmetric,fernet,hash,kdf, keyagreement,mac,padding,symmetric,wrapping} trees and the superseded PycaSecretContextTranslator now that everything has been moved to detection/pyca/ and replaced by the new translators. Signed-off-by: san-zrl --- .../contexts/PycaSecretContextTranslator.java | 63 ----- .../detection/aead/PycaAESGCMTestFile.py | 10 - .../aead/PycaChaCha20Poly1305TestFile.py | 11 - .../asymmetric/DSA/PycaDSANumbersTestFile.py | 13 - .../asymmetric/DSA/PycaDSASignTestFile.py | 11 - .../PycaDiffieHellmanGenerateTestFile.py | 6 - .../PycaDiffieHellmanNumbersTestFile.py | 13 - .../PycaEllipticCurveDeriveTestFile.py | 12 - .../PycaEllipticCurveKeyExchangeTestFile.py | 21 -- .../PycaEllipticCurveNumbersTestFile.py | 53 ---- .../PycaEllipticCurveSign2TestFile.py | 48 ---- .../PycaEllipticCurveSignTestFile.py | 37 --- .../PycaEllipticCurveVerifyTestFile.py | 8 - .../asymmetric/RSA/PycaRSADecryptTestFile.py | 19 -- .../asymmetric/RSA/PycaRSANumbersTestFile.py | 13 - .../asymmetric/RSA/PycaRSASign1TestFile.py | 19 -- .../asymmetric/RSA/PycaRSASign2TestFile.py | 16 -- .../fernet/PycaFernetDecryptTestFile.py | 15 -- .../fernet/PycaFernetEncryptTestFile.py | 16 -- .../fernet/PycaMultiFernetTestFile.py | 19 -- .../detection/hash/PycaHashDirectTest.py | 5 - .../kdf/PycaConcatKDFHMACTestFile.py | 15 -- .../kdf/PycaConcatKDFHashTestFile.py | 13 - .../detection/kdf/PycaHKDFExpandTestFile.py | 14 - .../rules/detection/kdf/PycaHKDFTestFile.py | 15 -- .../detection/kdf/PycaKBKDFCMACTestFile.py | 21 -- .../detection/kdf/PycaKBKDFHMACTestFile.py | 22 -- .../rules/detection/kdf/PycaPBKDF2TestFile.py | 16 -- .../rules/detection/kdf/PycaScryptTestFile.py | 15 -- .../detection/kdf/PycaX963KDFTestFile.py | 13 - .../keyagreement/PycaKeyAgreementTestFile.py | 38 --- .../rules/detection/mac/PycaCMACTestFile.py | 25 -- .../rules/detection/mac/PycaHMACTestFile.py | 25 -- ...ycaMacDetectionInCustomFunctionTestFile.py | 27 -- .../detection/mac/PycaPoly1305TestFile.py | 21 -- .../detection/padding/PycaPaddingTestFile.py | 16 -- .../symmetric/PycaCipher1TestFile.py | 21 -- .../symmetric/PycaCipher2TestFile.py | 12 - .../symmetric/PycaStreamCipher1TestFile.py | 11 - .../wrapping/PycaWrappingTestFile.py | 25 -- .../PycaWrappingWithPaddingTestFile.py | 25 -- .../rules/detection/aead/PycaAESGCMTest.java | 148 ---------- .../aead/PycaChaCha20Poly1305Test.java | 135 ---------- .../asymmetric/DSA/PycaDSANumbersTest.java | 139 ---------- .../asymmetric/DSA/PycaDSASignTest.java | 156 ----------- .../PycaDiffieHellmanGenerateTest.java | 99 ------- .../PycaDiffieHellmanNumbersTest.java | 96 ------- .../PycaEllipticCurveDeriveTest.java | 103 ------- .../PycaEllipticCurveKeyExchangeTest.java | 206 -------------- .../PycaEllipticCurveNumbersTest.java | 52 ---- .../PycaEllipticCurveSign2Test.java | 185 ------------- .../PycaEllipticCurveSignTest.java | 158 ----------- .../PycaEllipticCurveVerifyTest.java | 55 ---- .../asymmetric/RSA/PycaRSADecryptTest.java | 252 ------------------ .../asymmetric/RSA/PycaRSANumbersTest.java | 141 ---------- .../asymmetric/RSA/PycaRSASign1Test.java | 231 ---------------- .../asymmetric/RSA/PycaRSASign2Test.java | 170 ------------ .../fernet/PycaFernetDecryptTest.java | 200 -------------- .../fernet/PycaFernetEncryptTest.java | 200 -------------- .../detection/fernet/PycaMultiFernetTest.java | 211 --------------- .../detection/hash/PycaHashDirectTest.java | 102 ------- .../detection/kdf/PycaConcatKDFHMACTest.java | 133 --------- .../detection/kdf/PycaConcatKDFHashTest.java | 131 --------- .../detection/kdf/PycaHKDFExpandTest.java | 133 --------- .../rules/detection/kdf/PycaHKDFTest.java | 132 --------- .../detection/kdf/PycaKBKDFCMACTest.java | 142 ---------- .../detection/kdf/PycaKBKDFHMACTest.java | 163 ----------- .../rules/detection/kdf/PycaPBKDF2Test.java | 147 ---------- .../rules/detection/kdf/PycaScryptTest.java | 101 ------- .../rules/detection/kdf/PycaX963KDFTest.java | 133 --------- .../keyagreement/PycaKeyAgreementTest.java | 178 ------------- .../rules/detection/mac/PycaCMACTest.java | 100 ------- .../rules/detection/mac/PycaHMACTest.java | 121 --------- .../PycaMacDetectionInCustomFunctionTest.java | 131 --------- .../rules/detection/mac/PycaPoly1305Test.java | 94 ------- .../detection/padding/PycaPaddingTest.java | 125 --------- .../detection/symmetric/PycaCipher1Test.java | 161 ----------- .../detection/symmetric/PycaCipher2Test.java | 104 -------- .../symmetric/PycaStreamCipher1Test.java | 89 ------- .../detection/wrapping/PycaWrappingTest.java | 87 ------ .../wrapping/PycaWrappingWithPaddingTest.java | 87 ------ 81 files changed, 6349 deletions(-) delete mode 100644 python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaSecretContextTranslator.java delete mode 100644 python/src/test/files/rules/detection/aead/PycaAESGCMTestFile.py delete mode 100644 python/src/test/files/rules/detection/aead/PycaChaCha20Poly1305TestFile.py delete mode 100644 python/src/test/files/rules/detection/asymmetric/DSA/PycaDSANumbersTestFile.py delete mode 100644 python/src/test/files/rules/detection/asymmetric/DSA/PycaDSASignTestFile.py delete mode 100644 python/src/test/files/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTestFile.py delete mode 100644 python/src/test/files/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTestFile.py delete mode 100644 python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTestFile.py delete mode 100644 python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTestFile.py delete mode 100644 python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTestFile.py delete mode 100644 python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSign2TestFile.py delete mode 100644 python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSignTestFile.py delete mode 100644 python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTestFile.py delete mode 100644 python/src/test/files/rules/detection/asymmetric/RSA/PycaRSADecryptTestFile.py delete mode 100644 python/src/test/files/rules/detection/asymmetric/RSA/PycaRSANumbersTestFile.py delete mode 100644 python/src/test/files/rules/detection/asymmetric/RSA/PycaRSASign1TestFile.py delete mode 100644 python/src/test/files/rules/detection/asymmetric/RSA/PycaRSASign2TestFile.py delete mode 100644 python/src/test/files/rules/detection/fernet/PycaFernetDecryptTestFile.py delete mode 100644 python/src/test/files/rules/detection/fernet/PycaFernetEncryptTestFile.py delete mode 100644 python/src/test/files/rules/detection/fernet/PycaMultiFernetTestFile.py delete mode 100644 python/src/test/files/rules/detection/hash/PycaHashDirectTest.py delete mode 100644 python/src/test/files/rules/detection/kdf/PycaConcatKDFHMACTestFile.py delete mode 100644 python/src/test/files/rules/detection/kdf/PycaConcatKDFHashTestFile.py delete mode 100644 python/src/test/files/rules/detection/kdf/PycaHKDFExpandTestFile.py delete mode 100644 python/src/test/files/rules/detection/kdf/PycaHKDFTestFile.py delete mode 100644 python/src/test/files/rules/detection/kdf/PycaKBKDFCMACTestFile.py delete mode 100644 python/src/test/files/rules/detection/kdf/PycaKBKDFHMACTestFile.py delete mode 100644 python/src/test/files/rules/detection/kdf/PycaPBKDF2TestFile.py delete mode 100644 python/src/test/files/rules/detection/kdf/PycaScryptTestFile.py delete mode 100644 python/src/test/files/rules/detection/kdf/PycaX963KDFTestFile.py delete mode 100644 python/src/test/files/rules/detection/keyagreement/PycaKeyAgreementTestFile.py delete mode 100644 python/src/test/files/rules/detection/mac/PycaCMACTestFile.py delete mode 100644 python/src/test/files/rules/detection/mac/PycaHMACTestFile.py delete mode 100644 python/src/test/files/rules/detection/mac/PycaMacDetectionInCustomFunctionTestFile.py delete mode 100644 python/src/test/files/rules/detection/mac/PycaPoly1305TestFile.py delete mode 100644 python/src/test/files/rules/detection/padding/PycaPaddingTestFile.py delete mode 100644 python/src/test/files/rules/detection/symmetric/PycaCipher1TestFile.py delete mode 100644 python/src/test/files/rules/detection/symmetric/PycaCipher2TestFile.py delete mode 100644 python/src/test/files/rules/detection/symmetric/PycaStreamCipher1TestFile.py delete mode 100644 python/src/test/files/rules/detection/wrapping/PycaWrappingTestFile.py delete mode 100644 python/src/test/files/rules/detection/wrapping/PycaWrappingWithPaddingTestFile.py delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/aead/PycaAESGCMTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/aead/PycaChaCha20Poly1305Test.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DSA/PycaDSANumbersTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DSA/PycaDSASignTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSign2Test.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSignTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSADecryptTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSANumbersTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSASign1Test.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSASign2Test.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/fernet/PycaFernetDecryptTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/fernet/PycaFernetEncryptTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/fernet/PycaMultiFernetTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/hash/PycaHashDirectTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaConcatKDFHMACTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaConcatKDFHashTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaHKDFExpandTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaHKDFTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaKBKDFCMACTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaKBKDFHMACTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaPBKDF2Test.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaScryptTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaX963KDFTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/keyagreement/PycaKeyAgreementTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaCMACTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaHMACTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaMacDetectionInCustomFunctionTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaPoly1305Test.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/padding/PycaPaddingTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/symmetric/PycaCipher1Test.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/symmetric/PycaCipher2Test.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/symmetric/PycaStreamCipher1Test.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/wrapping/PycaWrappingTest.java delete mode 100644 python/src/test/java/com/ibm/plugin/rules/detection/wrapping/PycaWrappingWithPaddingTest.java diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaSecretContextTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaSecretContextTranslator.java deleted file mode 100644 index f85a24d14..000000000 --- a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaSecretContextTranslator.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.translation.translator.contexts; - -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeyAction; -import com.ibm.engine.model.context.DetectionContext; -import com.ibm.engine.model.context.IDetectionContext; -import com.ibm.engine.rule.IBundle; -import com.ibm.mapper.IContextTranslation; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.SecretKey; -import com.ibm.mapper.model.algorithms.Fernet; -import com.ibm.mapper.model.functionality.KeyGeneration; -import com.ibm.mapper.utils.DetectionLocation; -import java.util.Optional; -import javax.annotation.Nonnull; -import org.sonar.plugins.python.api.tree.Tree; - -public final class PycaSecretContextTranslator implements IContextTranslation { - @Override - public @Nonnull Optional translate( - @Nonnull IBundle bundleIdentifier, - @Nonnull IValue value, - @Nonnull IDetectionContext detectionContext, - @Nonnull DetectionLocation detectionLocation) { - if (value instanceof KeyAction - && detectionContext instanceof DetectionContext context) { - // action is always "generate" - return context.get("algorithm") - .map( - str -> - switch (str.toUpperCase().trim()) { - case "FERNET" -> new Fernet(detectionLocation); - default -> null; - }) - .map( - algo -> { - final SecretKey key = new SecretKey(algo); - key.put(new KeyGeneration(detectionLocation)); - return key; - }); - } - return Optional.empty(); - } -} diff --git a/python/src/test/files/rules/detection/aead/PycaAESGCMTestFile.py b/python/src/test/files/rules/detection/aead/PycaAESGCMTestFile.py deleted file mode 100644 index 84de0996e..000000000 --- a/python/src/test/files/rules/detection/aead/PycaAESGCMTestFile.py +++ /dev/null @@ -1,10 +0,0 @@ -import os -from cryptography.hazmat.primitives.ciphers.aead import AESGCM - -data = b"a secret message" -aad = b"authenticated but unencrypted data" -key = AESGCM.generate_key(bit_length=128) # Noncompliant {{(SecretKey) AES}} -aesgcm = AESGCM(key) -nonce = os.urandom(12) -ct = aesgcm.encrypt(nonce, data, aad) -aesgcm.decrypt(nonce, ct, aad) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/aead/PycaChaCha20Poly1305TestFile.py b/python/src/test/files/rules/detection/aead/PycaChaCha20Poly1305TestFile.py deleted file mode 100644 index 135304288..000000000 --- a/python/src/test/files/rules/detection/aead/PycaChaCha20Poly1305TestFile.py +++ /dev/null @@ -1,11 +0,0 @@ -import os -from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305 - -data = b"a secret message" -aad = b"authenticated but unencrypted data" -key = ChaCha20Poly1305.generate_key() # Noncompliant {{(SecretKey) ChaCha20}} -chacha = ChaCha20Poly1305(key) -nonce = os.urandom(12) -ct = chacha.encrypt(nonce, data, aad) -nonce2 = os.urandom(12) -chacha.decrypt(nonce2, ct, aad) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/asymmetric/DSA/PycaDSANumbersTestFile.py b/python/src/test/files/rules/detection/asymmetric/DSA/PycaDSANumbersTestFile.py deleted file mode 100644 index a2205d08d..000000000 --- a/python/src/test/files/rules/detection/asymmetric/DSA/PycaDSANumbersTestFile.py +++ /dev/null @@ -1,13 +0,0 @@ -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives.asymmetric import dsa -from cryptography.hazmat.primitives.asymmetric.dsa import DSAPrivateNumbers - -def generate_dsa_key_from_parameters( - p, q, g, x, y -) -> dsa.DSAPrivateKey: - """ - Generates a DSA private key from parameters p, q, g, x, and y. - """ - public_numbers = dsa.DSAPublicNumbers(y, dsa.DSAParameterNumbers(p, q, g)) # Noncompliant {{(PublicKey) DSA}} - private_numbers = DSAPrivateNumbers(x, public_numbers) # Noncompliant {{(PrivateKey) DSA}} - return private_numbers.private_key(default_backend()) diff --git a/python/src/test/files/rules/detection/asymmetric/DSA/PycaDSASignTestFile.py b/python/src/test/files/rules/detection/asymmetric/DSA/PycaDSASignTestFile.py deleted file mode 100644 index 4bed251fe..000000000 --- a/python/src/test/files/rules/detection/asymmetric/DSA/PycaDSASignTestFile.py +++ /dev/null @@ -1,11 +0,0 @@ -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric import dsa - -private_key = dsa.generate_private_key( # Noncompliant {{(PrivateKey) DSA}} - key_size=1024, -) -data = b"this is some data I'd like to sign" -signature = private_key.sign( - data, - hashes.SHA256() -) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTestFile.py b/python/src/test/files/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTestFile.py deleted file mode 100644 index ea45be61b..000000000 --- a/python/src/test/files/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTestFile.py +++ /dev/null @@ -1,6 +0,0 @@ -from cryptography.hazmat.primitives.asymmetric import dh - -# Generate some parameters. These can be reused. -parameters = dh.generate_parameters(generator=2, key_size=2048) -# Generate a private key for use in the exchange. -server_private_key = parameters.generate_private_key() # Noncompliant {{(PrivateKey) FFDH}} diff --git a/python/src/test/files/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTestFile.py b/python/src/test/files/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTestFile.py deleted file mode 100644 index 06a8ae83b..000000000 --- a/python/src/test/files/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTestFile.py +++ /dev/null @@ -1,13 +0,0 @@ -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives.asymmetric import dh -from cryptography.hazmat.primitives.asymmetric.dh import DHPrivateNumbers - -def generate_dh_key_from_parameters( - p, g, x, y -) -> dh.DHPrivateKey: - """ - Generates a DH private key from parameters p, g, x, and y. - """ - public_numbers = dh.DHPublicNumbers(y, p, g) # Noncompliant {{(PublicKey) FFDH}} - private_numbers = DHPrivateNumbers(x, public_numbers) # Noncompliant {{(PublicKey) FFDH}} - return private_numbers.private_key(default_backend()) diff --git a/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTestFile.py b/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTestFile.py deleted file mode 100644 index e5709f211..000000000 --- a/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTestFile.py +++ /dev/null @@ -1,12 +0,0 @@ -# Code inspired by https://github.com/dimaqq/minioidc/blob/main/tests/test_minioidc.py - -import cryptography.hazmat.primitives.asymmetric.ec -import base64 - -TEST_PRIVATE_KEY = cryptography.hazmat.primitives.asymmetric.ec.derive_private_key( # Noncompliant {{(PrivateKey) EC-secp256r1}} - int.from_bytes( - base64.urlsafe_b64decode("870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE" + "==="), - "big", - ), - cryptography.hazmat.primitives.asymmetric.ec.SECP256R1(), -) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTestFile.py b/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTestFile.py deleted file mode 100644 index b1d727b32..000000000 --- a/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTestFile.py +++ /dev/null @@ -1,21 +0,0 @@ -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric import ec -from cryptography.hazmat.primitives.kdf.hkdf import HKDF - -# Generate a private key for use in the exchange. -server_private_key = ec.generate_private_key( # Noncompliant {{(PrivateKey) EC-secp384r1}} - ec.SECP384R1() -) - -def exchange(public_key): - shared_key = server_private_key.exchange( - ec.ECDH(), public_key) - - # Perform key derivation. // TODO: How should this key derivation be linked to the private key? - derived_key = HKDF( # Noncompliant {{(KeyDerivationFunction) HKDF-SHA-256}} - algorithm=hashes.SHA256(), - length=32, - salt=None, - info=b'handshake data', - ).derive(shared_key) - return derived_key \ No newline at end of file diff --git a/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTestFile.py b/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTestFile.py deleted file mode 100644 index 38c65d80d..000000000 --- a/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTestFile.py +++ /dev/null @@ -1,53 +0,0 @@ -# Code inspired by https://github.com/ydb-platform/ydb/blob/284b7efb67edcdade0b12c849b7fad40739ad62b/contrib/python/Twisted/py2/twisted/conch/ssh/keys.py#L799 - -from cryptography.hazmat.primitives.asymmetric import dsa, rsa, padding, ec - -_curveTable = { - b'ecdsa-sha2-nistp256': ec.SECP256R1(), - b'ecdsa-sha2-nistp384': ec.SECP384R1(), - b'ecdsa-sha2-nistp521': ec.SECP521R1(), -} - -def default_backend(): - global _default_backend - - if _default_backend is None: - from cryptography.hazmat.backends.openssl.backend import backend - - _default_backend = backend - - return _default_backend - -class Key(object): - @classmethod - def _fromECComponents(cls, x, y, curve, privateValue=None): - """ - Build a key from EC components. - - @param x: The affine x component of the public point used for verifying. - @type x: L{int} - - @param y: The affine y component of the public point used for verifying. - @type y: L{int} - - @param curve: NIST name of elliptic curve. - @type curve: L{bytes} - - @param privateValue: The private value. - @type privateValue: L{int} - """ - - publicNumbers = ec.EllipticCurvePublicNumbers( - x=x, y=y, curve=_curveTable[curve]) - if privateValue is None: - # We have public components. - keyObject = publicNumbers.public_key(default_backend()) - else: - privateNumbers = ec.EllipticCurvePrivateNumbers( - private_value=privateValue, public_numbers=publicNumbers) - keyObject = privateNumbers.private_key(default_backend()) - - return cls(keyObject) - -some_var = b'ecdsa-sha2-nistp256' -Key._fromECComponents(None, None, None, some_var, None) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSign2TestFile.py b/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSign2TestFile.py deleted file mode 100644 index 56aab2093..000000000 --- a/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSign2TestFile.py +++ /dev/null @@ -1,48 +0,0 @@ -from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey -from cryptography.hazmat.primitives.asymmetric.ed448 import Ed448PrivateKey - -private_key = Ed25519PrivateKey.generate() # Noncompliant {{(PrivateKey) Ed25519}} -signature = private_key.sign(b"my authenticated message") -public_key = private_key.public_key() -# Raises InvalidSignature if verification fails -public_key.verify(signature, b"my authenticated message") - -private_key = Ed448PrivateKey.generate() # Noncompliant {{(PrivateKey) Ed448}} -signature = private_key.sign(b"my authenticated message") -public_key = private_key.public_key() -# Raises InvalidSignature if verification fails -public_key.verify(signature, b"my authenticated message") - -# False positives that should NOT be detected (PR-429 fix) -# These are unrelated generate() methods with parameters -class VLMModel: - def generate(self, **gen_kwargs): - return [1, 2, 3] - -class TextModel: - def generate(self, *prompts): - return "generated text" - -vlm_model = VLMModel() -text_model = TextModel() - -# These should NOT trigger detection (not cryptography-related) -generated_ids = vlm_model.generate(**{"max_length": 100}) -generated_text = text_model.generate("prompt1", "prompt2") - -# GROUND TRUTH (translation of the 1st finding) -# -# PrivateKey EC -# Signature EdDSA -# MessageDigest SHA-512 -# EllipticCurveAlgorithm EC -# EllipticCurve Curve25519 -# Sign SIGN -# EllipticCurveAlgorithm EC -# EllipticCurve Curve25519 -# KeyGeneration KEYGENERATION -# PublicKey EC -# EllipticCurveAlgorithm EC -# EllipticCurve Curve25519 -# KeyGeneration KEYGENERATION -# \ No newline at end of file diff --git a/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSignTestFile.py b/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSignTestFile.py deleted file mode 100644 index 56fdeec3d..000000000 --- a/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSignTestFile.py +++ /dev/null @@ -1,37 +0,0 @@ -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric import ec -from cryptography.hazmat.primitives.asymmetric import utils - -# param = ec.SECP192R1() -param = ec.SECP384R1() -# private_key, other_var = ec.generate_private_key(param), 42 # TODO: because of TraceSeymbols not yet supporting multi-var assignments, this does not work -private_key = ec.generate_private_key(param) # Noncompliant {{(PrivateKey) EC-secp384r1}} - -# Ploys that should not be detected -b = ec.ECDSA(utils.Prehashed(hashes.SHA3_224())) # TODO: The test should pass also when removing "b =" -utils.Prehashed(hashes.SHA3_224()) -hashes.SHA3_224() - -digest = b"\x00" * 64 -sig = private_key.sign(digest, ec.ECDSA(utils.Prehashed(hashes.SHA3_512()))) - -# TODO: Make it work when uncommented -# pk = private_key.public_key() -# pk.verify(sig, digest, ec.ECDSA(hashes.SHA3_512())) - -# GROUND TRUTH (translation) -# -# PrivateKey EC -# Signature ECDSA -# MessageDigest SHA3-512 -# EllipticCurveAlgorithm EC -# EllipticCurve SECP384R1 -# Sign SIGN -# EllipticCurveAlgorithm EC -# EllipticCurve SECP384R1 -# KeyGeneration KEYGENERATION -# PublicKey EC -# EllipticCurveAlgorithm EC -# EllipticCurve SECP384R1 -# KeyGeneration KEYGENERATION -# diff --git a/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTestFile.py b/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTestFile.py deleted file mode 100644 index c357dea0f..000000000 --- a/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTestFile.py +++ /dev/null @@ -1,8 +0,0 @@ -# Code inspired by https://github.com/redis/redis-py/blob/master/redis/ocsp.py - -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric import ec - -def verify(pubkey, signature, digest): - if isinstance(pubkey, ec.EllipticCurvePublicKey): - pubkey.verify(signature, digest, ec.ECDSA(hashes.SHA3_512())) diff --git a/python/src/test/files/rules/detection/asymmetric/RSA/PycaRSADecryptTestFile.py b/python/src/test/files/rules/detection/asymmetric/RSA/PycaRSADecryptTestFile.py deleted file mode 100644 index a63b81590..000000000 --- a/python/src/test/files/rules/detection/asymmetric/RSA/PycaRSADecryptTestFile.py +++ /dev/null @@ -1,19 +0,0 @@ -from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric import padding - -private_key = rsa.generate_private_key( # Noncompliant {{(PrivateKey) RSA}} - public_exponent=65537, - key_size=1024, -) - -def decrypt(ciphertext): - plaintext = private_key.decrypt( - ciphertext, - padding.OAEP( - mgf=padding.MGF1(algorithm=hashes.SHA384()), - algorithm=hashes.SHA256(), - label=None - ) - ) - return plaintext \ No newline at end of file diff --git a/python/src/test/files/rules/detection/asymmetric/RSA/PycaRSANumbersTestFile.py b/python/src/test/files/rules/detection/asymmetric/RSA/PycaRSANumbersTestFile.py deleted file mode 100644 index f84461215..000000000 --- a/python/src/test/files/rules/detection/asymmetric/RSA/PycaRSANumbersTestFile.py +++ /dev/null @@ -1,13 +0,0 @@ -from cryptography.hazmat.primitives.asymmetric.rsa import * -from cryptography.hazmat.backends import default_backend - -def generate_rsa_key_from_parameters( - p, q, d, dmp1, dmq1, iqmp, e, n -) -> RSAPrivateKey: - """ - Note: from certbot dp is dmp1, dq is dmq1 and qi is iqmp - """ - public_numbers = RSAPublicNumbers(e, n) # Noncompliant {{(PublicKey) RSA}} - return RSAPrivateNumbers( # Noncompliant {{(PrivateKey) RSA}} - p, q, d, dmp1, dmq1, iqmp, public_numbers - ).private_key(default_backend()) diff --git a/python/src/test/files/rules/detection/asymmetric/RSA/PycaRSASign1TestFile.py b/python/src/test/files/rules/detection/asymmetric/RSA/PycaRSASign1TestFile.py deleted file mode 100644 index 3d25ad2dd..000000000 --- a/python/src/test/files/rules/detection/asymmetric/RSA/PycaRSASign1TestFile.py +++ /dev/null @@ -1,19 +0,0 @@ -from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric import padding -from cryptography.hazmat.primitives.asymmetric import utils - -private_key = rsa.generate_private_key( # Noncompliant {{(PrivateKey) RSA}} - public_exponent=65537, - key_size=2048, -) - -message = b"A message I want to sign" -signature = private_key.sign( - message, - padding.PSS( - mgf=padding.MGF1(hashes.SHA256()), - salt_length=padding.PSS.MAX_LENGTH - ), - utils.Prehashed(hashes.SHA384()) -) diff --git a/python/src/test/files/rules/detection/asymmetric/RSA/PycaRSASign2TestFile.py b/python/src/test/files/rules/detection/asymmetric/RSA/PycaRSASign2TestFile.py deleted file mode 100644 index 43fd0ae39..000000000 --- a/python/src/test/files/rules/detection/asymmetric/RSA/PycaRSASign2TestFile.py +++ /dev/null @@ -1,16 +0,0 @@ -from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric import padding -from cryptography.hazmat.primitives.asymmetric import utils - -private_key = rsa.generate_private_key( # Noncompliant {{(PrivateKey) RSA}} - public_exponent=65537, - key_size=2048, -) - -message = b"A message I want to sign" -signature = private_key.sign( - message, - padding.PKCS1v15(), - hashes.SHA3_384() -) diff --git a/python/src/test/files/rules/detection/fernet/PycaFernetDecryptTestFile.py b/python/src/test/files/rules/detection/fernet/PycaFernetDecryptTestFile.py deleted file mode 100644 index 0ba848d20..000000000 --- a/python/src/test/files/rules/detection/fernet/PycaFernetDecryptTestFile.py +++ /dev/null @@ -1,15 +0,0 @@ -from cryptography.fernet import Fernet - -def test1(): - key = Fernet.generate_key() # Noncompliant {{(SecretKey) Fernet}} - - def dec(ciphertext): - f = Fernet(key) - return f.decrypt(ciphertext) - -def test2(): - key = Fernet.generate_key() # Noncompliant {{(SecretKey) Fernet}} - - def dec(ciphertext, time): - f = Fernet(key) - return f.decrypt_at_time(ciphertext, time) diff --git a/python/src/test/files/rules/detection/fernet/PycaFernetEncryptTestFile.py b/python/src/test/files/rules/detection/fernet/PycaFernetEncryptTestFile.py deleted file mode 100644 index e7b3eeb58..000000000 --- a/python/src/test/files/rules/detection/fernet/PycaFernetEncryptTestFile.py +++ /dev/null @@ -1,16 +0,0 @@ -from cryptography.fernet import Fernet - -def test1(): - key = Fernet.generate_key() # Noncompliant {{(SecretKey) Fernet}} - - def enc(data): - f = Fernet(key) - return f.encrypt(data) - -def test2(): - key = Fernet.generate_key() # Noncompliant {{(SecretKey) Fernet}} - - def enc(data, time): - f = Fernet(key) - return f.encrypt_at_time(data, time) - diff --git a/python/src/test/files/rules/detection/fernet/PycaMultiFernetTestFile.py b/python/src/test/files/rules/detection/fernet/PycaMultiFernetTestFile.py deleted file mode 100644 index 669b8f0d4..000000000 --- a/python/src/test/files/rules/detection/fernet/PycaMultiFernetTestFile.py +++ /dev/null @@ -1,19 +0,0 @@ -from cryptography.fernet import Fernet, MultiFernet - -key1 = Fernet(Fernet.generate_key()) # Noncompliant {{(SecretKey) Fernet}} -key2 = Fernet(Fernet.generate_key()) # Noncompliant {{(SecretKey) Fernet}} - -def enc(data): - return MultiFernet([key1, key2]).encrypt(data) - -def dec(data): - return MultiFernet([key1, key2]).decrypt(data) - -# TODO: When using the following code instead, the depending `encrypt` and `decrypt` are detected twice because the type resolution of `f` does not succeed (so it could be Fernet as well as MultiFernet) - -# f = MultiFernet([key1, key2]) -# def enc(data): -# return f.encrypt(data) - -# def dec(data): -# return f.decrypt(data) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/hash/PycaHashDirectTest.py b/python/src/test/files/rules/detection/hash/PycaHashDirectTest.py deleted file mode 100644 index 3b6afb9c1..000000000 --- a/python/src/test/files/rules/detection/hash/PycaHashDirectTest.py +++ /dev/null @@ -1,5 +0,0 @@ -from cryptography.hazmat.primitives import hashes - -sha256_obj = hashes.Hash(hashes.SHA256()) # Noncompliant {{(MessageDigest) SHA-256}} -sha256_obj.update(b"data") -digest = sha256_obj.finalize() diff --git a/python/src/test/files/rules/detection/kdf/PycaConcatKDFHMACTestFile.py b/python/src/test/files/rules/detection/kdf/PycaConcatKDFHMACTestFile.py deleted file mode 100644 index bdd95889d..000000000 --- a/python/src/test/files/rules/detection/kdf/PycaConcatKDFHMACTestFile.py +++ /dev/null @@ -1,15 +0,0 @@ -import os -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.kdf.concatkdf import ConcatKDFHMAC - -salt = os.urandom(16) -otherinfo = b"concatkdf-example" - -ckdf = ConcatKDFHMAC( # Noncompliant {{(KeyDerivationFunction) ConcatenationKDF}} - algorithm=hashes.SHA256(), - length=32, - salt=salt, - otherinfo=otherinfo, -) - -key = ckdf.derive(b"input key") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/kdf/PycaConcatKDFHashTestFile.py b/python/src/test/files/rules/detection/kdf/PycaConcatKDFHashTestFile.py deleted file mode 100644 index f4be827c0..000000000 --- a/python/src/test/files/rules/detection/kdf/PycaConcatKDFHashTestFile.py +++ /dev/null @@ -1,13 +0,0 @@ -import os -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.kdf.concatkdf import ConcatKDFHash - -otherinfo = b"concatkdf-example" - -ckdf = ConcatKDFHash( # Noncompliant {{(KeyDerivationFunction) ConcatenationKDF}} - algorithm=hashes.SHA256(), - length=64, - otherinfo=otherinfo, -) - -key = ckdf.derive(b"input key") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/kdf/PycaHKDFExpandTestFile.py b/python/src/test/files/rules/detection/kdf/PycaHKDFExpandTestFile.py deleted file mode 100644 index 081088184..000000000 --- a/python/src/test/files/rules/detection/kdf/PycaHKDFExpandTestFile.py +++ /dev/null @@ -1,14 +0,0 @@ -import os -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.kdf.hkdf import HKDFExpand - -info = b"hkdf-example" -key_material = os.urandom(16) - -hkdf = HKDFExpand( # Noncompliant {{(KeyDerivationFunction) HKDF-SHA-256}} - algorithm=hashes.SHA256(), - length=32, - info=info, -) - -key = hkdf.derive(key_material) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/kdf/PycaHKDFTestFile.py b/python/src/test/files/rules/detection/kdf/PycaHKDFTestFile.py deleted file mode 100644 index eb0a673b0..000000000 --- a/python/src/test/files/rules/detection/kdf/PycaHKDFTestFile.py +++ /dev/null @@ -1,15 +0,0 @@ -import os -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.kdf.hkdf import HKDF - -salt = os.urandom(16) -info = b"hkdf-example" - -hkdf = HKDF( # Noncompliant {{(KeyDerivationFunction) HKDF-SHA-256}} - algorithm=hashes.SHA256(), - length=32, - salt=salt, - info=info, -) - -key = hkdf.derive(b"input key") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/kdf/PycaKBKDFCMACTestFile.py b/python/src/test/files/rules/detection/kdf/PycaKBKDFCMACTestFile.py deleted file mode 100644 index abd8f7afb..000000000 --- a/python/src/test/files/rules/detection/kdf/PycaKBKDFCMACTestFile.py +++ /dev/null @@ -1,21 +0,0 @@ -from cryptography.hazmat.primitives.ciphers import algorithms -from cryptography.hazmat.primitives.kdf.kbkdf import ( - CounterLocation, KBKDFCMAC, Mode -) - -label = b"KBKDF CMAC Label" -context = b"KBKDF CMAC Context" - -kdf = KBKDFCMAC( # Noncompliant {{(Mac) CMAC-AES}} - algorithm=algorithms.AES, - mode=Mode.CounterMode, - length=32, - rlen=4, - llen=4, - location=CounterLocation.BeforeFixed, - label=label, - context=context, - fixed=None, -) - -key = kdf.derive(b"32 bytes long input key material") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/kdf/PycaKBKDFHMACTestFile.py b/python/src/test/files/rules/detection/kdf/PycaKBKDFHMACTestFile.py deleted file mode 100644 index f29af21d6..000000000 --- a/python/src/test/files/rules/detection/kdf/PycaKBKDFHMACTestFile.py +++ /dev/null @@ -1,22 +0,0 @@ -import os -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.kdf.kbkdf import ( - CounterLocation, KBKDFHMAC, Mode -) - -label = b"KBKDF HMAC Label" -context = b"KBKDF HMAC Context" - -kdf = KBKDFHMAC( # Noncompliant {{(Mac) HMAC-SHA-256}} - algorithm=hashes.SHA256(), - mode=Mode.CounterMode, - length=32, - rlen=4, - llen=4, - location=CounterLocation.BeforeFixed, - label=label, - context=context, - fixed=None, -) - -key = kdf.derive(b"input key") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/kdf/PycaPBKDF2TestFile.py b/python/src/test/files/rules/detection/kdf/PycaPBKDF2TestFile.py deleted file mode 100644 index 6baeb886c..000000000 --- a/python/src/test/files/rules/detection/kdf/PycaPBKDF2TestFile.py +++ /dev/null @@ -1,16 +0,0 @@ -import os -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC - -# Salts should be randomly generated -salt = os.urandom(16) - -# derive -kdf = PBKDF2HMAC( # Noncompliant {{(PasswordBasedKeyDerivationFunction) PBKDF2-SHA-256}} - algorithm=hashes.SHA256(), - length=32, - salt=salt, - iterations=480000, -) - -key = kdf.derive(b"my great password") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/kdf/PycaScryptTestFile.py b/python/src/test/files/rules/detection/kdf/PycaScryptTestFile.py deleted file mode 100644 index 42c5ad086..000000000 --- a/python/src/test/files/rules/detection/kdf/PycaScryptTestFile.py +++ /dev/null @@ -1,15 +0,0 @@ -import os -from cryptography.hazmat.primitives.kdf.scrypt import Scrypt - -salt = os.urandom(16) - -# derive -kdf = Scrypt( # Noncompliant {{(PasswordBasedKeyDerivationFunction) scrypt}} - salt=salt, - length=32, - n=2**14, - r=8, - p=1, -) - -key = kdf.derive(b"my great password") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/kdf/PycaX963KDFTestFile.py b/python/src/test/files/rules/detection/kdf/PycaX963KDFTestFile.py deleted file mode 100644 index 10c0f51c7..000000000 --- a/python/src/test/files/rules/detection/kdf/PycaX963KDFTestFile.py +++ /dev/null @@ -1,13 +0,0 @@ -import os -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.kdf.x963kdf import X963KDF - -sharedinfo = b"ANSI-KDF-X9.63 Example" - -xkdf = X963KDF( # Noncompliant {{(KeyDerivationFunction) ANSI-KDF-X9.63}} - algorithm=hashes.SHA256(), - length=32, - sharedinfo=sharedinfo, -) - -key = xkdf.derive(b"input key") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/keyagreement/PycaKeyAgreementTestFile.py b/python/src/test/files/rules/detection/keyagreement/PycaKeyAgreementTestFile.py deleted file mode 100644 index f3212f37a..000000000 --- a/python/src/test/files/rules/detection/keyagreement/PycaKeyAgreementTestFile.py +++ /dev/null @@ -1,38 +0,0 @@ -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey -from cryptography.hazmat.primitives.asymmetric.x448 import X448PrivateKey - -# Generate a private key for use in the exchange. -private_key = X25519PrivateKey.generate() # Noncompliant {{(KeyAgreement) x25519}} -# In a real handshake the peer_public_key will be received from the -# other party. For this example we'll generate another private key and -# get a public key from that. Note that in a DH handshake both peers -# must agree on a common set of parameters. -peer_public_key = X25519PrivateKey.generate().public_key() # Noncompliant {{(KeyAgreement) x25519}} -shared_key = private_key.exchange(peer_public_key) - -# Generate a private key for use in the exchange. -private_key = X448PrivateKey.generate() # Noncompliant {{(KeyAgreement) x448}} -# In a real handshake the peer_public_key will be received from the -# other party. For this example we'll generate another private key and -# get a public key from that. Note that in a DH handshake both peers -# must agree on a common set of parameters. -peer_public_key = X448PrivateKey.generate().public_key() # Noncompliant {{(KeyAgreement) x448}} -shared_key = private_key.exchange(peer_public_key) - -# False positives that should NOT be detected (PR-429 fix) -# These are unrelated generate() methods with parameters -class DataGenerator: - def generate(self, **kwargs): - return [1, 2, 3] - -class ModelGenerator: - def generate(self, *args): - return "generated" - -data_gen = DataGenerator() -model_gen = ModelGenerator() - -# These should NOT trigger detection (not cryptography-related) -result1 = data_gen.generate(**{"size": 100}) -result2 = model_gen.generate("prompt1", "prompt2") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/mac/PycaCMACTestFile.py b/python/src/test/files/rules/detection/mac/PycaCMACTestFile.py deleted file mode 100644 index 1d86fa264..000000000 --- a/python/src/test/files/rules/detection/mac/PycaCMACTestFile.py +++ /dev/null @@ -1,25 +0,0 @@ -from cryptography.hazmat.primitives import cmac -from cryptography.hazmat.primitives.ciphers import algorithms - -def generate_cmac(key, data): - # Selecting the desired algorithm (e.g., CMAC-AES) - algorithm = algorithms.AES(key) - - # Creating the CMAC context - cmac_ctx = cmac.CMAC(algorithm) # Noncompliant {{(Mac) CMAC-AES}} - - # Updating the context with the data - cmac_ctx.update(data) - - # Finalizing the CMAC computation and getting the CMAC value - cmac_value = cmac_ctx.finalize() - - return cmac_value - -# Example usage -if __name__ == "__main__": - key = b'Sixteen byte key' # 16-byte key for AES - data = b'This is some data' # Data to generate CMAC for - - cmac_value = generate_cmac(key, data) - print("Generated CMAC:", cmac_value.hex()) diff --git a/python/src/test/files/rules/detection/mac/PycaHMACTestFile.py b/python/src/test/files/rules/detection/mac/PycaHMACTestFile.py deleted file mode 100644 index 70490920a..000000000 --- a/python/src/test/files/rules/detection/mac/PycaHMACTestFile.py +++ /dev/null @@ -1,25 +0,0 @@ -from cryptography.hazmat.primitives import hmac -from cryptography.hazmat.primitives import hashes - -def generate_hmac(key, data): - # Selecting the desired hash algorithm (e.g., SHA-256) - algorithm = hashes.SHA256() - - # Creating the HMAC context - hmac_ctx = hmac.HMAC(key, algorithm) # Noncompliant {{(Mac) HMAC-SHA-256}} - - # Updating the context with the data - hmac_ctx.update(data) - - # Finalizing the HMAC computation and getting the HMAC value - hmac_value = hmac_ctx.finalize() - - return hmac_value - -# Example usage -if __name__ == "__main__": - key = b'SecretKey123' # Key for HMAC - data = b'This is some data' # Data to generate HMAC for - - hmac_value = generate_hmac(key, data) - print("Generated HMAC:", hmac_value.hex()) diff --git a/python/src/test/files/rules/detection/mac/PycaMacDetectionInCustomFunctionTestFile.py b/python/src/test/files/rules/detection/mac/PycaMacDetectionInCustomFunctionTestFile.py deleted file mode 100644 index ec0a20fc1..000000000 --- a/python/src/test/files/rules/detection/mac/PycaMacDetectionInCustomFunctionTestFile.py +++ /dev/null @@ -1,27 +0,0 @@ -from cryptography.hazmat.primitives import hmac -from cryptography.hazmat.primitives import hashes - -def custom_sign(key, data): - # Custom function with cryptographic operation - algorithm = hashes.SHA256() - hmac_obj = hmac.HMAC(key, algorithm) # Noncompliant {{(Mac) HMAC-SHA-256}} - hmac_obj.update(data) - return hmac_obj.finalize() - -def non_crypto_function(text): - # Non-cryptographic function - should not trigger detection - result = "not crypto: " + text - return result.upper() - -# Example usage -if __name__ == "__main__": - key = b'SecretKey123' - data = b'This is some data' - - # Cryptographic operation in custom function is detected - result = custom_sign(key, data) - print("HMAC Result:", result.hex()) - - # Non-cryptographic function call does not trigger detection - text_result = non_crypto_function("hello") - print("Text Result:", text_result) diff --git a/python/src/test/files/rules/detection/mac/PycaPoly1305TestFile.py b/python/src/test/files/rules/detection/mac/PycaPoly1305TestFile.py deleted file mode 100644 index b51c92c91..000000000 --- a/python/src/test/files/rules/detection/mac/PycaPoly1305TestFile.py +++ /dev/null @@ -1,21 +0,0 @@ -from cryptography.hazmat.primitives.poly1305 import Poly1305 - -def generate_poly1305(key, data): - # Create a Poly1305 context with the given key - poly1305_ctx = Poly1305(key) # Noncompliant {{(Mac) HMAC-Poly1305}} - - # Update the context with the data - poly1305_ctx.update(data) - - # Finalize the Poly1305 computation and get the authentication tag - poly1305_tag = poly1305_ctx.finalize() - - return poly1305_tag - -# Example usage -if __name__ == "__main__": - key = b'Sixteen byte key' # 16-byte key for Poly1305 - data = b'This is some data' # Data to generate Poly1305 tag for - - poly1305_tag = generate_poly1305(key, data) - print("Generated Poly1305 Tag:", poly1305_tag.hex()) diff --git a/python/src/test/files/rules/detection/padding/PycaPaddingTestFile.py b/python/src/test/files/rules/detection/padding/PycaPaddingTestFile.py deleted file mode 100644 index 7485f8b52..000000000 --- a/python/src/test/files/rules/detection/padding/PycaPaddingTestFile.py +++ /dev/null @@ -1,16 +0,0 @@ -import os -from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes -from cryptography.hazmat.primitives import padding - -key = os.urandom(32) -iv = os.urandom(16) -# Create a cipher object -cipher = Cipher(algorithms.CAST5(key), modes.CFB(iv)) # Noncompliant {{(BlockCipher) CAST5-CFB}} - -padder = padding.ANSIX923(128).padder() -padded_data = padder.update(b"a secret message") -print(padded_data) -padded_data += padder.finalize() -print(padded_data) - -# Then, one could use the cipher to encrypt the padded data \ No newline at end of file diff --git a/python/src/test/files/rules/detection/symmetric/PycaCipher1TestFile.py b/python/src/test/files/rules/detection/symmetric/PycaCipher1TestFile.py deleted file mode 100644 index b95d70dac..000000000 --- a/python/src/test/files/rules/detection/symmetric/PycaCipher1TestFile.py +++ /dev/null @@ -1,21 +0,0 @@ -import os -from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes -from cryptography.hazmat.primitives.padding import PKCS7 - -key = os.urandom(32) -iv = os.urandom(16) -# Create a cipher object -cipher = Cipher(algorithms.AES(key), modes.CBC(iv)) # Noncompliant {{(BlockCipher) AES-CBC-PKCS7}} - -# Specify padding (PKCS7 in this case) -padder = PKCS7(algorithms.AES.block_size).padder() - -# Encrypt -encryptor = cipher.encryptor() -padded_data = padder.update(b"a secret message") + padder.finalize() -ct = encryptor.update(padded_data) + encryptor.finalize() - -# Decrypt -decryptor = cipher.decryptor() -padded_res = decryptor.update(ct) + decryptor.finalize() -unpadded_res = padder.update(padded_res) + padder.finalize() \ No newline at end of file diff --git a/python/src/test/files/rules/detection/symmetric/PycaCipher2TestFile.py b/python/src/test/files/rules/detection/symmetric/PycaCipher2TestFile.py deleted file mode 100644 index 3372719a6..000000000 --- a/python/src/test/files/rules/detection/symmetric/PycaCipher2TestFile.py +++ /dev/null @@ -1,12 +0,0 @@ -import os -from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes -from cryptography.hazmat.primitives.padding import PKCS7 - -key = os.urandom(32) -iv = os.urandom(16) -# Create a cipher object -cipher = Cipher(algorithms.Camellia(key), modes.OFB(iv)) # Noncompliant {{(BlockCipher) CAMELLIA-OFB}} - -# Encrypt -encryptor = cipher.encryptor() -ct = encryptor.update(b"a secret message") + encryptor.finalize() diff --git a/python/src/test/files/rules/detection/symmetric/PycaStreamCipher1TestFile.py b/python/src/test/files/rules/detection/symmetric/PycaStreamCipher1TestFile.py deleted file mode 100644 index a1ff78550..000000000 --- a/python/src/test/files/rules/detection/symmetric/PycaStreamCipher1TestFile.py +++ /dev/null @@ -1,11 +0,0 @@ -import os -from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes - -key = os.urandom(32) -iv = os.urandom(16) -# Create a cipher object -cipher = Cipher(algorithms.ChaCha20(key, nonce), mode=None) # Noncompliant {{(StreamCipher) ChaCha20}} - -# Encrypt -encryptor = cipher.encryptor() -ct = encryptor.update(b"a secret message") + encryptor.finalize() diff --git a/python/src/test/files/rules/detection/wrapping/PycaWrappingTestFile.py b/python/src/test/files/rules/detection/wrapping/PycaWrappingTestFile.py deleted file mode 100644 index f4b3093e4..000000000 --- a/python/src/test/files/rules/detection/wrapping/PycaWrappingTestFile.py +++ /dev/null @@ -1,25 +0,0 @@ -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives.keywrap import aes_key_wrap, aes_key_unwrap - -def aes_key_wrap_example(): - # Generate a key to wrap - key_to_wrap = b'Sixteen byte key' - - # Generate wrapping key (must be 128, 192, or 256 bits long) - wrapping_key = b'ABCDEFGHIJKLMNOP' - - # Wrap the key - wrapped_key = aes_key_wrap(wrapping_key, key_to_wrap, default_backend()) # Noncompliant {{(KeyWrap) AES-128}} - - print("Wrapped Key:", wrapped_key.hex()) - - # Unwrap the key - unwrapped_key = aes_key_unwrap(wrapping_key, wrapped_key, default_backend()) - - print("Unwrapped Key:", unwrapped_key.hex()) - - # Ensure that the unwrapped key matches the original key - assert unwrapped_key == key_to_wrap - -if __name__ == "__main__": - aes_key_wrap_example() diff --git a/python/src/test/files/rules/detection/wrapping/PycaWrappingWithPaddingTestFile.py b/python/src/test/files/rules/detection/wrapping/PycaWrappingWithPaddingTestFile.py deleted file mode 100644 index 62580615e..000000000 --- a/python/src/test/files/rules/detection/wrapping/PycaWrappingWithPaddingTestFile.py +++ /dev/null @@ -1,25 +0,0 @@ -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives.keywrap import aes_key_wrap_with_padding, aes_key_unwrap_with_padding - -def aes_key_wrap_example(): - # Generate a key to wrap - key_to_wrap = b'Sixteen byte key' - - # Generate wrapping key (must be 128, 192, or 256 bits long) - wrapping_key = b'ABCDEFGHIJKLMNOP' - - # Wrap the key - wrapped_key = aes_key_wrap_with_padding(wrapping_key, key_to_wrap, default_backend()) # Noncompliant {{(KeyWrap) AES-128}} - - print("Wrapped Key:", wrapped_key.hex()) - - # Unwrap the key - unwrapped_key = aes_key_unwrap_with_padding(wrapping_key, wrapped_key, default_backend()) - - print("Unwrapped Key:", unwrapped_key.hex()) - - # Ensure that the unwrapped key matches the original key - assert unwrapped_key == key_to_wrap - -if __name__ == "__main__": - aes_key_wrap_example() diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/aead/PycaAESGCMTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/aead/PycaAESGCMTest.java deleted file mode 100644 index d3409b362..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/aead/PycaAESGCMTest.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.aead; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.CipherAction; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeySize; -import com.ibm.engine.model.context.CipherContext; -import com.ibm.engine.model.context.SecretKeyContext; -import com.ibm.mapper.model.AuthenticatedEncryption; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.KeyLength; -import com.ibm.mapper.model.Mode; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.SecretKey; -import com.ibm.mapper.model.functionality.Decrypt; -import com.ibm.mapper.model.functionality.Encrypt; -import com.ibm.mapper.model.functionality.KeyGeneration; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaAESGCMTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/aead/PycaAESGCMTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - IValue value = detectionStore.getDetectionValues().get(0); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(SecretKeyContext.class); - assertThat(value).isInstanceOf(KeySize.class); - assertThat(value.asString()).isEqualTo("128"); - - assertThat(detectionStore.getChildren()).hasSize(2); - - DetectionStore store = - detectionStore.getChildren().get(0); - IValue decryptValue = store.getDetectionValues().get(0); - assertThat(store.getDetectionValueContext()).isInstanceOf(CipherContext.class); - assertThat(decryptValue).isInstanceOf(CipherAction.class); - assertThat(decryptValue.asString()).isEqualTo("DECRYPT"); - - store = detectionStore.getChildren().get(1); - IValue encryptValue = store.getDetectionValues().get(0); - assertThat(store.getDetectionValueContext()).isInstanceOf(CipherContext.class); - assertThat(encryptValue).isInstanceOf(CipherAction.class); - assertThat(encryptValue.asString()).isEqualTo("ENCRYPT"); - - /* - * Translation - */ - - assertThat(nodes).hasSize(1); - - // SecretKey - INode secretKeyNode = nodes.get(0); - assertThat(secretKeyNode.getKind()).isEqualTo(SecretKey.class); - assertThat(secretKeyNode.getChildren()).hasSize(4); - assertThat(secretKeyNode.asString()).isEqualTo("AES"); - - // AuthenticatedEncryption under SecretKey - INode authenticatedEncryptionNode = - secretKeyNode.getChildren().get(AuthenticatedEncryption.class); - assertThat(authenticatedEncryptionNode).isNotNull(); - assertThat(authenticatedEncryptionNode.getChildren()).hasSize(4); - assertThat(authenticatedEncryptionNode.asString()).isEqualTo("AES-128-GCM"); - - // Mode under AuthenticatedEncryption under SecretKey - INode modeNode = authenticatedEncryptionNode.getChildren().get(Mode.class); - assertThat(modeNode).isNotNull(); - assertThat(modeNode.getChildren()).isEmpty(); - assertThat(modeNode.asString()).isEqualTo("GCM"); - - // BlockSize under AuthenticatedEncryption under SecretKey - INode blockSizeNode = authenticatedEncryptionNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("128"); - - // Oid under AuthenticatedEncryption under SecretKey - INode oidNode = authenticatedEncryptionNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.1.6"); - - // KeyLength under AuthenticatedEncryption under SecretKey - INode keyLengthNode = authenticatedEncryptionNode.getChildren().get(KeyLength.class); - assertThat(keyLengthNode).isNotNull(); - assertThat(keyLengthNode.getChildren()).isEmpty(); - assertThat(keyLengthNode.asString()).isEqualTo("128"); - - // Encrypt under SecretKey - INode encryptNode = secretKeyNode.getChildren().get(Encrypt.class); - assertThat(encryptNode).isNotNull(); - assertThat(encryptNode.getChildren()).isEmpty(); - assertThat(encryptNode.asString()).isEqualTo("ENCRYPT"); - - // Decrypt under SecretKey - INode decryptNode = secretKeyNode.getChildren().get(Decrypt.class); - assertThat(decryptNode).isNotNull(); - assertThat(decryptNode.getChildren()).isEmpty(); - assertThat(decryptNode.asString()).isEqualTo("DECRYPT"); - - // Generate under SecretKey - INode generateNode = secretKeyNode.getChildren().get(KeyGeneration.class); - assertThat(generateNode).isNotNull(); - assertThat(generateNode.getChildren()).isEmpty(); - assertThat(generateNode.asString()).isEqualTo("KEYGENERATION"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/aead/PycaChaCha20Poly1305Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/aead/PycaChaCha20Poly1305Test.java deleted file mode 100644 index 004201c31..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/aead/PycaChaCha20Poly1305Test.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.aead; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.CipherAction; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeyAction; -import com.ibm.engine.model.context.CipherContext; -import com.ibm.engine.model.context.SecretKeyContext; -import com.ibm.mapper.model.AuthenticatedEncryption; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.SecretKey; -import com.ibm.mapper.model.functionality.Decrypt; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.mapper.model.functionality.Encrypt; -import com.ibm.mapper.model.functionality.KeyGeneration; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaChaCha20Poly1305Test extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/aead/PycaChaCha20Poly1305TestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - IValue value = detectionStore.getDetectionValues().get(0); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(SecretKeyContext.class); - assertThat(value).isInstanceOf(KeyAction.class); - assertThat(value.asString()).isEqualTo("GENERATION"); - - assertThat(detectionStore.getChildren()).hasSize(2); - - DetectionStore store = - detectionStore.getChildren().get(0); - IValue decryptValue = store.getDetectionValues().get(0); - assertThat(store.getDetectionValueContext()).isInstanceOf(CipherContext.class); - assertThat(decryptValue).isInstanceOf(CipherAction.class); - assertThat(decryptValue.asString()).isEqualTo("ENCRYPT"); - - store = detectionStore.getChildren().get(1); - IValue encryptValue = store.getDetectionValues().get(0); - assertThat(store.getDetectionValueContext()).isInstanceOf(CipherContext.class); - assertThat(encryptValue).isInstanceOf(CipherAction.class); - assertThat(encryptValue.asString()).isEqualTo("DECRYPT"); - - /* - * Translation - */ - - assertThat(nodes).hasSize(1); - - // SecretKey - INode secretKeyNode = nodes.get(0); - assertThat(secretKeyNode.getKind()).isEqualTo(SecretKey.class); - assertThat(secretKeyNode.getChildren()).hasSize(4); - assertThat(secretKeyNode.asString()).isEqualTo("ChaCha20"); - - // Encrypt under SecretKey - INode encryptNode = secretKeyNode.getChildren().get(Encrypt.class); - assertThat(encryptNode).isNotNull(); - assertThat(encryptNode.getChildren()).isEmpty(); - assertThat(encryptNode.asString()).isEqualTo("ENCRYPT"); - - // Decrypt under SecretKey - INode decryptNode = secretKeyNode.getChildren().get(Decrypt.class); - assertThat(decryptNode).isNotNull(); - assertThat(decryptNode.getChildren()).isEmpty(); - assertThat(decryptNode.asString()).isEqualTo("DECRYPT"); - - // KeyGeneration under SecretKey - INode keyGenerationNode = secretKeyNode.getChildren().get(KeyGeneration.class); - assertThat(keyGenerationNode).isNotNull(); - assertThat(keyGenerationNode.getChildren()).isEmpty(); - assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); - - // AuthenticatedEncryption under SecretKey - INode authenticatedEncryptionNode = - secretKeyNode.getChildren().get(AuthenticatedEncryption.class); - assertThat(authenticatedEncryptionNode).isNotNull(); - assertThat(authenticatedEncryptionNode.getChildren()).hasSize(1); - assertThat(authenticatedEncryptionNode.asString()).isEqualTo("ChaCha20-Poly1305"); - - // MessageDigest under AuthenticatedEncryption under SecretKey - INode messageDigestNode = - authenticatedEncryptionNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(1); - assertThat(messageDigestNode.asString()).isEqualTo("Poly1305"); - - // Digest under MessageDigest under AuthenticatedEncryption under SecretKey - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DSA/PycaDSANumbersTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DSA/PycaDSANumbersTest.java deleted file mode 100644 index 2178efc29..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DSA/PycaDSANumbersTest.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.asymmetric.DSA; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeyAction; -import com.ibm.engine.model.context.PrivateKeyContext; -import com.ibm.engine.model.context.PublicKeyContext; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.PrivateKey; -import com.ibm.mapper.model.PublicKey; -import com.ibm.mapper.model.Signature; -import com.ibm.mapper.model.functionality.KeyGeneration; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaDSANumbersTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/DSA/PycaDSANumbersTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - - if (findingId == 0) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()) - .isInstanceOf(PublicKeyContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(KeyAction.class); - assertThat(value0.asString()).isEqualTo("GENERATION"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // PublicKey - INode publicKeyNode = nodes.get(0); - assertThat(publicKeyNode.getKind()).isEqualTo(PublicKey.class); - assertThat(publicKeyNode.getChildren()).hasSize(2); - assertThat(publicKeyNode.asString()).isEqualTo("DSA"); - - // Signature under PublicKey - INode signatureNode = publicKeyNode.getChildren().get(Signature.class); - assertThat(signatureNode).isNotNull(); - assertThat(signatureNode.getChildren()).hasSize(1); - assertThat(signatureNode.asString()).isEqualTo("DSA"); - - // Oid under Signature under PublicKey - INode oidNode = signatureNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("1.2.840.10040.4.1"); - - // KeyGeneration under PublicKey - INode keyGenerationNode = publicKeyNode.getChildren().get(KeyGeneration.class); - assertThat(keyGenerationNode).isNotNull(); - assertThat(keyGenerationNode.getChildren()).isEmpty(); - assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); - } else { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()) - .isInstanceOf(PrivateKeyContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(KeyAction.class); - assertThat(value0.asString()).isEqualTo("GENERATION"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // PrivateKey - INode privateKeyNode = nodes.get(0); - assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); - assertThat(privateKeyNode.getChildren()).hasSize(2); - assertThat(privateKeyNode.asString()).isEqualTo("DSA"); - - // Signature under PrivateKey - INode signatureNode = privateKeyNode.getChildren().get(Signature.class); - assertThat(signatureNode).isNotNull(); - assertThat(signatureNode.getChildren()).hasSize(1); - assertThat(signatureNode.asString()).isEqualTo("DSA"); - - // Oid under Signature under PrivateKey - INode oidNode = signatureNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("1.2.840.10040.4.1"); - - // KeyGeneration under PrivateKey - INode keyGenerationNode = privateKeyNode.getChildren().get(KeyGeneration.class); - assertThat(keyGenerationNode).isNotNull(); - assertThat(keyGenerationNode.getChildren()).isEmpty(); - assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); - } - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DSA/PycaDSASignTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DSA/PycaDSASignTest.java deleted file mode 100644 index 295196b08..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DSA/PycaDSASignTest.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.asymmetric.DSA; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeySize; -import com.ibm.engine.model.SignatureAction; -import com.ibm.engine.model.ValueAction; -import com.ibm.engine.model.context.DigestContext; -import com.ibm.engine.model.context.PrivateKeyContext; -import com.ibm.engine.model.context.SignatureContext; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.DigestSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.PrivateKey; -import com.ibm.mapper.model.Signature; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.mapper.model.functionality.KeyGeneration; -import com.ibm.mapper.model.functionality.Sign; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaDSASignTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/DSA/PycaDSASignTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(PrivateKeyContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(KeySize.class); - assertThat(value0.asString()).isEqualTo("1024"); - - DetectionStore store_1 = - getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); - assertThat(store_1.getDetectionValues()).hasSize(1); - assertThat(store_1.getDetectionValueContext()).isInstanceOf(SignatureContext.class); - IValue value0_1 = store_1.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(SignatureAction.class); - assertThat(value0_1.asString()).isEqualTo("SIGN"); - - DetectionStore store_1_1 = - getStoreOfValueType(ValueAction.class, store_1.getChildren()); - assertThat(store_1_1.getDetectionValues()).hasSize(1); - assertThat(store_1_1.getDetectionValueContext()).isInstanceOf(DigestContext.class); - IValue value0_1_1 = store_1_1.getDetectionValues().get(0); - assertThat(value0_1_1).isInstanceOf(ValueAction.class); - assertThat(value0_1_1.asString()).isEqualTo("SHA256"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // PrivateKey - INode privateKeyNode = nodes.get(0); - assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); - assertThat(privateKeyNode.getChildren()).hasSize(4); - assertThat(privateKeyNode.asString()).isEqualTo("DSA"); - - // Signature under PrivateKey - INode signatureNode = privateKeyNode.getChildren().get(Signature.class); - assertThat(signatureNode).isNotNull(); - assertThat(signatureNode.getChildren()).hasSize(2); - assertThat(signatureNode.asString()).isEqualTo("DSA-SHA-256"); - - // Oid under Signature under PrivateKey - INode oidNode = signatureNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.3.2"); - - // MessageDigest under Signature under PrivateKey - INode messageDigestNode = signatureNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(4); - assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); - - // BlockSize under MessageDigest under Signature under PrivateKey - INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("512"); - - // Oid under MessageDigest under Signature under PrivateKey - INode oidNode1 = messageDigestNode.getChildren().get(Oid.class); - assertThat(oidNode1).isNotNull(); - assertThat(oidNode1.getChildren()).isEmpty(); - assertThat(oidNode1.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); - - // DigestSize under MessageDigest under Signature under PrivateKey - INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); - assertThat(digestSizeNode).isNotNull(); - assertThat(digestSizeNode.getChildren()).isEmpty(); - assertThat(digestSizeNode.asString()).isEqualTo("256"); - - // Digest under MessageDigest under Signature under PrivateKey - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - - // KeyGeneration under PrivateKey - INode keyGenerationNode = privateKeyNode.getChildren().get(KeyGeneration.class); - assertThat(keyGenerationNode).isNotNull(); - assertThat(keyGenerationNode.getChildren()).isEmpty(); - assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); - - // Sign under PrivateKey - INode signNode = privateKeyNode.getChildren().get(Sign.class); - assertThat(signNode).isNotNull(); - assertThat(signNode.getChildren()).isEmpty(); - assertThat(signNode.asString()).isEqualTo("SIGN"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTest.java deleted file mode 100644 index 06ce44018..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.asymmetric.DiffieHellman; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeyAction; -import com.ibm.engine.model.context.PrivateKeyContext; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.PrivateKey; -import com.ibm.mapper.model.PublicKeyEncryption; -import com.ibm.mapper.model.functionality.KeyGeneration; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -public final class PycaDiffieHellmanGenerateTest extends TestBase { - - // The key size does not yet appear because - // of the TraceSymbol problem documented on the Github issue - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTestFile.py", - this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(PrivateKeyContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(KeyAction.class); - assertThat(value0.asString()).isEqualTo("GENERATION"); - - /* - * Translation - */ - - assertThat(nodes).hasSize(1); - - // PrivateKey - INode privateKeyNode = nodes.get(0); - assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); - assertThat(privateKeyNode.getChildren()).hasSize(2); - assertThat(privateKeyNode.asString()).isEqualTo("FFDH"); - - // KeyGeneration under PrivateKey - INode keyGenerationNode = privateKeyNode.getChildren().get(KeyGeneration.class); - assertThat(keyGenerationNode).isNotNull(); - assertThat(keyGenerationNode.getChildren()).isEmpty(); - assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); - - // PublicKeyEncryption under PrivateKey - INode publicKeyEncryptionNode = privateKeyNode.getChildren().get(PublicKeyEncryption.class); - assertThat(publicKeyEncryptionNode).isNotNull(); - assertThat(publicKeyEncryptionNode.getChildren()).hasSize(1); - assertThat(publicKeyEncryptionNode.asString()).isEqualTo("FFDH"); - - // Oid under PublicKeyEncryption under PrivateKey - INode oidNode = publicKeyEncryptionNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("1.2.840.113549.1.3.1"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTest.java deleted file mode 100644 index bdd8bf7dc..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.asymmetric.DiffieHellman; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeyAction; -import com.ibm.engine.model.context.PublicKeyContext; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.PublicKey; -import com.ibm.mapper.model.PublicKeyEncryption; -import com.ibm.mapper.model.functionality.KeyGeneration; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaDiffieHellmanNumbersTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTestFile.py", - this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(PublicKeyContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(KeyAction.class); - assertThat(value0.asString()).isEqualTo("GENERATION"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // PublicKey - INode publicKeyNode1 = nodes.get(0); - assertThat(publicKeyNode1.getKind()).isEqualTo(PublicKey.class); - assertThat(publicKeyNode1.getChildren()).hasSize(2); - assertThat(publicKeyNode1.asString()).isEqualTo("FFDH"); - - // PublicKeyEncryption under PublicKey - INode publicKeyEncryptionNode1 = - publicKeyNode1.getChildren().get(PublicKeyEncryption.class); - assertThat(publicKeyEncryptionNode1).isNotNull(); - assertThat(publicKeyEncryptionNode1.getChildren()).hasSize(1); - assertThat(publicKeyEncryptionNode1.asString()).isEqualTo("FFDH"); - - // Oid under PublicKeyEncryption under PublicKey - INode oidNode1 = publicKeyEncryptionNode1.getChildren().get(Oid.class); - assertThat(oidNode1).isNotNull(); - assertThat(oidNode1.getChildren()).isEmpty(); - assertThat(oidNode1.asString()).isEqualTo("1.2.840.113549.1.3.1"); - - // KeyGeneration under PublicKey - INode keyGenerationNode1 = publicKeyNode1.getChildren().get(KeyGeneration.class); - assertThat(keyGenerationNode1).isNotNull(); - assertThat(keyGenerationNode1.getChildren()).isEmpty(); - assertThat(keyGenerationNode1.asString()).isEqualTo("KEYGENERATION"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTest.java deleted file mode 100644 index dde89a44a..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTest.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.asymmetric.EllipticCurve; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.Curve; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.context.PrivateKeyContext; -import com.ibm.mapper.model.EllipticCurve; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.PrivateKey; -import com.ibm.mapper.model.PublicKeyEncryption; -import com.ibm.mapper.model.functionality.KeyGeneration; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaEllipticCurveDeriveTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTestFile.py", - this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - IValue value = detectionStore.getDetectionValues().get(0); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(PrivateKeyContext.class); - assertThat(value).isInstanceOf(Curve.class); - assertThat(value.asString()).isEqualTo("SECP256R1"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // PrivateKey - INode privateKeyNode = nodes.get(0); - assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); - assertThat(privateKeyNode.getChildren()).hasSize(2); - assertThat(privateKeyNode.asString()).isEqualTo("EC-secp256r1"); - - // KeyGeneration under PrivateKey - INode keyGenerationNode = privateKeyNode.getChildren().get(KeyGeneration.class); - assertThat(keyGenerationNode).isNotNull(); - assertThat(keyGenerationNode.getChildren()).isEmpty(); - assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); - - // PublicKeyEncryption under PrivateKey - INode publicKeyEncryptionNode = privateKeyNode.getChildren().get(PublicKeyEncryption.class); - assertThat(publicKeyEncryptionNode).isNotNull(); - assertThat(publicKeyEncryptionNode.getChildren()).hasSize(2); - assertThat(publicKeyEncryptionNode.asString()).isEqualTo("EC-secp256r1"); - - // EllipticCurve under PublicKeyEncryption under PrivateKey - INode ellipticCurveNode = publicKeyEncryptionNode.getChildren().get(EllipticCurve.class); - assertThat(ellipticCurveNode).isNotNull(); - assertThat(ellipticCurveNode.getChildren()).isEmpty(); - assertThat(ellipticCurveNode.asString()).isEqualTo("secp256r1"); - - // Oid under PublicKeyEncryption under PrivateKey - INode oidNode = publicKeyEncryptionNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("1.2.840.10045.2.1"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTest.java deleted file mode 100644 index ca421610e..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTest.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.asymmetric.EllipticCurve; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.Algorithm; -import com.ibm.engine.model.Curve; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeySize; -import com.ibm.engine.model.context.KeyAgreementContext; -import com.ibm.engine.model.context.KeyDerivationFunctionContext; -import com.ibm.engine.model.context.PrivateKeyContext; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.DigestSize; -import com.ibm.mapper.model.EllipticCurve; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.KeyAgreement; -import com.ibm.mapper.model.KeyDerivationFunction; -import com.ibm.mapper.model.KeyLength; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.PrivateKey; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.mapper.model.functionality.KeyDerivation; -import com.ibm.mapper.model.functionality.KeyGeneration; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaEllipticCurveKeyExchangeTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTestFile.py", - this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - if (findingId == 0) { - /* - * Detection Store - */ - - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()) - .isInstanceOf(PrivateKeyContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(Curve.class); - assertThat(value0.asString()).isEqualTo("SECP384R1"); - - DetectionStore store_1 = - getStoreOfValueType(Algorithm.class, detectionStore.getChildren()); - assertThat(store_1.getDetectionValues()).hasSize(1); - assertThat(store_1.getDetectionValueContext()).isInstanceOf(KeyAgreementContext.class); - IValue value0_1 = store_1.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(Algorithm.class); - assertThat(value0_1.asString()).isEqualTo("ECDH"); - - /* - * Translation - */ - - assertThat(nodes).hasSize(1); - - // PrivateKey - INode privateKeyNode = nodes.get(0); - assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); - assertThat(privateKeyNode.getChildren()).hasSize(2); - assertThat(privateKeyNode.asString()).isEqualTo("EC-secp384r1"); - - // KeyGeneration under PrivateKey - INode keyGenerationNode = privateKeyNode.getChildren().get(KeyGeneration.class); - assertThat(keyGenerationNode).isNotNull(); - assertThat(keyGenerationNode.getChildren()).isEmpty(); - assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); - - // KeyAgreement under PrivateKey - INode keyAgreementNode = privateKeyNode.getChildren().get(KeyAgreement.class); - assertThat(keyAgreementNode).isNotNull(); - assertThat(keyAgreementNode.getChildren()).hasSize(3); - assertThat(keyAgreementNode.asString()).isEqualTo("ECDH"); - - // EllipticCurve under KeyAgreement under PrivateKey - INode ellipticCurveNode = keyAgreementNode.getChildren().get(EllipticCurve.class); - assertThat(ellipticCurveNode).isNotNull(); - assertThat(ellipticCurveNode.getChildren()).isEmpty(); - assertThat(ellipticCurveNode.asString()).isEqualTo("secp384r1"); - - // Oid under KeyAgreement under PrivateKey - INode oidNode = keyAgreementNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("1.3.132.1.12"); - - // KeyGeneration under PrivateKey - keyGenerationNode = keyAgreementNode.getChildren().get(KeyGeneration.class); - assertThat(keyGenerationNode).isNotNull(); - assertThat(keyGenerationNode.getChildren()).isEmpty(); - assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); - } else if (findingId == 1) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(Algorithm.class); - assertThat(value0.asString()).isEqualTo("SHA256"); - - DetectionStore store_1 = - getStoreOfValueType(KeySize.class, detectionStore.getChildren()); - assertThat(store_1.getDetectionValues()).hasSize(1); - assertThat(store_1.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0_1 = store_1.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(KeySize.class); - assertThat(value0_1.asString()).isEqualTo("256"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // KeyDerivationFunction - INode keyDerivationFunctionNode = nodes.get(0); - assertThat(keyDerivationFunctionNode.getKind()).isEqualTo(KeyDerivationFunction.class); - assertThat(keyDerivationFunctionNode.getChildren()).hasSize(3); - assertThat(keyDerivationFunctionNode.asString()).isEqualTo("HKDF-SHA-256"); - - // KeyDerivation under KeyDerivationFunction - INode keyDerivationNode = - keyDerivationFunctionNode.getChildren().get(KeyDerivation.class); - assertThat(keyDerivationNode).isNotNull(); - assertThat(keyDerivationNode.getChildren()).isEmpty(); - assertThat(keyDerivationNode.asString()).isEqualTo("KEYDERIVATION"); - - // KeyLength under KeyDerivationFunction - INode keyLengthNode = keyDerivationFunctionNode.getChildren().get(KeyLength.class); - assertThat(keyLengthNode).isNotNull(); - assertThat(keyLengthNode.getChildren()).isEmpty(); - assertThat(keyLengthNode.asString()).isEqualTo("256"); - - // MessageDigest under KeyDerivationFunction - INode messageDigestNode = - keyDerivationFunctionNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(4); - assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); - - // Digest under MessageDigest under KeyDerivationFunction - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - - // BlockSize under MessageDigest under KeyDerivationFunction - INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("512"); - - // Oid under MessageDigest under KeyDerivationFunction - INode oidNode = messageDigestNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); - - // DigestSize under MessageDigest under KeyDerivationFunction - INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); - assertThat(digestSizeNode).isNotNull(); - assertThat(digestSizeNode.getChildren()).isEmpty(); - assertThat(digestSizeNode.asString()).isEqualTo("256"); - } - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTest.java deleted file mode 100644 index 2c95fc794..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.asymmetric.EllipticCurve; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.mapper.model.INode; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.Ignore; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaEllipticCurveNumbersTest extends TestBase { - - @Ignore("In this testcase the name of a var is resolved, but not teh actual value.") - @Test - void test() { - PythonCheckVerifier.verifyNoIssue( - "src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTestFile.py", - this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - // TODO: - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSign2Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSign2Test.java deleted file mode 100644 index c6156730e..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSign2Test.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.asymmetric.EllipticCurve; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeyAction; -import com.ibm.engine.model.context.PrivateKeyContext; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.DigestSize; -import com.ibm.mapper.model.EllipticCurve; -import com.ibm.mapper.model.ExtendableOutputFunction; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.PrivateKey; -import com.ibm.mapper.model.Signature; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaEllipticCurveSign2Test extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSign2TestFile.py", - this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - if (findingId == 0) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()) - .isInstanceOf(PrivateKeyContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(KeyAction.class); - assertThat(value0.asString()).isEqualTo("GENERATION"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // PrivateKey - INode privateKeyNode = nodes.get(0); - assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); - assertThat(privateKeyNode.getChildren()).hasSize(2); - assertThat(privateKeyNode.asString()).isEqualTo("Ed25519"); - - // Signature under PrivateKey - INode signatureNode = privateKeyNode.getChildren().get(Signature.class); - assertThat(signatureNode).isNotNull(); - assertThat(signatureNode.getChildren()).hasSize(3); - assertThat(signatureNode.asString()).isEqualTo("Ed25519"); - - // MessageDigest under Signature under PrivateKey - INode messageDigestNode = signatureNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(4); - assertThat(messageDigestNode.asString()).isEqualTo("SHA-512"); - - // Oid under MessageDigest under Signature under PrivateKey - INode oidNode = messageDigestNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.3"); - - // DigestSize under MessageDigest under Signature under PrivateKey - INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); - assertThat(digestSizeNode).isNotNull(); - assertThat(digestSizeNode.getChildren()).isEmpty(); - assertThat(digestSizeNode.asString()).isEqualTo("512"); - - // Digest under MessageDigest under Signature under PrivateKey - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - - // BlockSize under MessageDigest under Signature under PrivateKey - INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("1024"); - - // Oid under Signature under PrivateKey - INode oidNode1 = signatureNode.getChildren().get(Oid.class); - assertThat(oidNode1).isNotNull(); - assertThat(oidNode1.getChildren()).isEmpty(); - assertThat(oidNode1.asString()).isEqualTo("1.3.101.112"); - - // EllipticCurve under Signature under PrivateKey - INode ellipticCurveNode = signatureNode.getChildren().get(EllipticCurve.class); - assertThat(ellipticCurveNode).isNotNull(); - assertThat(ellipticCurveNode.getChildren()).isEmpty(); - assertThat(ellipticCurveNode.asString()).isEqualTo("Edwards25519"); - } else if (findingId == 1) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()) - .isInstanceOf(PrivateKeyContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(KeyAction.class); - assertThat(value0.asString()).isEqualTo("GENERATION"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // PrivateKey - INode privateKeyNode = nodes.get(0); - assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); - assertThat(privateKeyNode.getChildren()).hasSize(2); - assertThat(privateKeyNode.asString()).isEqualTo("Ed448"); - - // Signature under PrivateKey - INode signatureNode = privateKeyNode.getChildren().get(Signature.class); - assertThat(signatureNode).isNotNull(); - assertThat(signatureNode.getChildren()).hasSize(3); - assertThat(signatureNode.asString()).isEqualTo("Ed448"); - - // MessageDigest under Signature under PrivateKey - INode messageDigestNode = - signatureNode.getChildren().get(ExtendableOutputFunction.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(2); - assertThat(messageDigestNode.asString()).isEqualTo("SHAKE256"); - - // Digest under MessageDigest under Signature under PrivateKey - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - - // Oid under Signature under PrivateKey - INode oidNode = signatureNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("1.3.101.113"); - - // EllipticCurve under Signature under PrivateKey - INode ellipticCurveNode = signatureNode.getChildren().get(EllipticCurve.class); - assertThat(ellipticCurveNode).isNotNull(); - assertThat(ellipticCurveNode.getChildren()).isEmpty(); - assertThat(ellipticCurveNode.asString()).isEqualTo("Edwards448"); - } - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSignTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSignTest.java deleted file mode 100644 index 42e6c8347..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSignTest.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.asymmetric.EllipticCurve; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.Algorithm; -import com.ibm.engine.model.Curve; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.SignatureAction; -import com.ibm.engine.model.context.PrivateKeyContext; -import com.ibm.engine.model.context.SignatureContext; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.DigestSize; -import com.ibm.mapper.model.EllipticCurve; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.PrivateKey; -import com.ibm.mapper.model.Signature; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.mapper.model.functionality.KeyGeneration; -import com.ibm.mapper.model.functionality.Sign; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaEllipticCurveSignTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSignTestFile.py", - this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(PrivateKeyContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(Curve.class); - assertThat(value0.asString()).isEqualTo("SECP384R1"); - - DetectionStore store_1 = - getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); - assertThat(store_1.getDetectionValues()).hasSize(2); - assertThat(store_1.getDetectionValueContext()).isInstanceOf(SignatureContext.class); - IValue value0_1 = store_1.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(SignatureAction.class); - assertThat(value0_1.asString()).isEqualTo("SIGN"); - - IValue value1_1 = store_1.getDetectionValues().get(1); - assertThat(value1_1).isInstanceOf(Algorithm.class); - assertThat(value1_1.asString()).isEqualTo("ECDSA"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // PrivateKey - INode privateKeyNode = nodes.get(0); - assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); - assertThat(privateKeyNode.getChildren()).hasSize(3); - assertThat(privateKeyNode.asString()).isEqualTo("EC-secp384r1"); - - // Signature under PrivateKey - INode signatureNode = privateKeyNode.getChildren().get(Signature.class); - assertThat(signatureNode).isNotNull(); - assertThat(signatureNode.getChildren()).hasSize(3); - assertThat(signatureNode.asString()).isEqualTo("ECDSA-secp384r1-SHA3-512"); - - // MessageDigest under Signature under PrivateKey - INode messageDigestNode = signatureNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(4); - assertThat(messageDigestNode.asString()).isEqualTo("SHA3-512"); - - // BlockSize under MessageDigest under Signature under PrivateKey - INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("576"); - - // Oid under MessageDigest under Signature under PrivateKey - INode oidNode = messageDigestNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.10"); - - // DigestSize under MessageDigest under Signature under PrivateKey - INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); - assertThat(digestSizeNode).isNotNull(); - assertThat(digestSizeNode.getChildren()).isEmpty(); - assertThat(digestSizeNode.asString()).isEqualTo("512"); - - // Digest under MessageDigest under Signature under PrivateKey - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - - // Oid under Signature under PrivateKey - INode oidNode1 = signatureNode.getChildren().get(Oid.class); - assertThat(oidNode1).isNotNull(); - assertThat(oidNode1.getChildren()).isEmpty(); - assertThat(oidNode1.asString()).isEqualTo("2.16.840.1.101.3.4.3.12"); - - // EllipticCurve under Signature under PrivateKey - INode ellipticCurveNode = signatureNode.getChildren().get(EllipticCurve.class); - assertThat(ellipticCurveNode).isNotNull(); - assertThat(ellipticCurveNode.getChildren()).isEmpty(); - assertThat(ellipticCurveNode.asString()).isEqualTo("secp384r1"); - - // Sign under PrivateKey - INode signNode = privateKeyNode.getChildren().get(Sign.class); - assertThat(signNode).isNotNull(); - assertThat(signNode.getChildren()).isEmpty(); - assertThat(signNode.asString()).isEqualTo("SIGN"); - - // KeyGeneration under PrivateKey - INode keyGenerationNode = privateKeyNode.getChildren().get(KeyGeneration.class); - assertThat(keyGenerationNode).isNotNull(); - assertThat(keyGenerationNode.getChildren()).isEmpty(); - assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTest.java deleted file mode 100644 index ab38d7bcb..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.asymmetric.EllipticCurve; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.mapper.model.INode; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaEllipticCurveVerifyTest extends TestBase { - - // junit4 - @Disabled( - "Reenable once we have an approach to detect `verify` (either make it an entry\n" - + "point, or better handle file imports for depending detection rule)") - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTestFile.py", - this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - // TODO - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSADecryptTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSADecryptTest.java deleted file mode 100644 index 416e98ba3..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSADecryptTest.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.asymmetric.RSA; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.CipherAction; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeySize; -import com.ibm.engine.model.ValueAction; -import com.ibm.engine.model.context.CipherContext; -import com.ibm.engine.model.context.DigestContext; -import com.ibm.engine.model.context.PrivateKeyContext; -import com.ibm.engine.model.context.SignatureContext; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.DigestSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.KeyLength; -import com.ibm.mapper.model.MaskGenerationFunction; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.Padding; -import com.ibm.mapper.model.PrivateKey; -import com.ibm.mapper.model.PublicKeyEncryption; -import com.ibm.mapper.model.functionality.Decrypt; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.mapper.model.functionality.KeyGeneration; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaRSADecryptTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/RSA/PycaRSADecryptTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - if (findingId == 0) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()) - .isInstanceOf(PrivateKeyContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(KeySize.class); - assertThat(value0.asString()).isEqualTo("1024"); - - DetectionStore store_1 = - getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); - assertThat(store_1).isNotNull(); - assertThat(store_1.getDetectionValues()).hasSize(1); - assertThat(store_1.getDetectionValueContext()).isInstanceOf(CipherContext.class); - IValue value0_1 = store_1.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(CipherAction.class); - assertThat(value0_1.asString()).isEqualTo("DECRYPT"); - - DetectionStore store_1_1 = - getStoreOfValueType(ValueAction.class, store_1.getChildren()); - assertThat(store_1_1).isNotNull(); - assertThat(store_1_1.getDetectionValues()).hasSize(1); - assertThat(store_1_1.getDetectionValueContext()).isInstanceOf(CipherContext.class); - IValue value0_1_1 = store_1_1.getDetectionValues().get(0); - assertThat(value0_1_1).isInstanceOf(ValueAction.class); - assertThat(value0_1_1.asString()).isEqualTo("OAEP"); - - List> stores = - getStoresOfValueType(ValueAction.class, store_1_1.getChildren()); - assertThat(stores).isNotNull(); - for (DetectionStore s : stores) { - assertThat(s.getDetectionValues()).hasSize(1); - assertThat(s.getDetectionValueContext()) - .isInstanceOfAny(SignatureContext.class, DigestContext.class); - IValue v = s.getDetectionValues().get(0); - assertThat(v).isInstanceOf(ValueAction.class); - assertThat(v.asString()) - .satisfiesAnyOf( - str -> assertThat(str).isEqualTo("MGF1"), - str -> assertThat(str).isEqualTo("SHA256")); - - if (s.getDetectionValueContext().is(SignatureContext.class)) { - DetectionStore store_1_1_1_1 = - getStoreOfValueType(ValueAction.class, s.getChildren()); - assertThat(store_1_1_1_1).isNotNull(); - assertThat(store_1_1_1_1.getDetectionValues()).hasSize(1); - assertThat(store_1_1_1_1.getDetectionValueContext()) - .isInstanceOf(DigestContext.class); - IValue value0_1_1_1_1 = store_1_1_1_1.getDetectionValues().get(0); - assertThat(value0_1_1_1_1).isInstanceOf(ValueAction.class); - assertThat(value0_1_1_1_1.asString()).isEqualTo("SHA384"); - } - } - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // PrivateKey - INode privateKeyNode = nodes.get(0); - assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); - assertThat(privateKeyNode.getChildren()).hasSize(4); - assertThat(privateKeyNode.asString()).isEqualTo("RSA"); - - // KeyGeneration under PrivateKey - INode keyGenerationNode = privateKeyNode.getChildren().get(KeyGeneration.class); - assertThat(keyGenerationNode).isNotNull(); - assertThat(keyGenerationNode.getChildren()).isEmpty(); - assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); - - // PublicKeyEncryption under PrivateKey - INode pke = privateKeyNode.getChildren().get(PublicKeyEncryption.class); - assertThat(pke).isNotNull(); - assertThat(pke.getChildren()).hasSize(2); - assertThat(pke.asString()).isEqualTo("RSA-OAEP"); - - // Oid under Signature under PrivateKey - INode oidNode = pke.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("1.2.840.113549.1.1.7"); - - // Padding under Signature under PrivateKey - INode paddingNode = pke.getChildren().get(Padding.class); - assertThat(paddingNode).isNotNull(); - assertThat(paddingNode.getChildren()).hasSize(2); - assertThat(paddingNode.asString()).isEqualTo("OAEP"); - - // MessageDigest under Padding under Signature under PrivateKey - INode messageDigestNode = paddingNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(4); - assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); - - // Oid under MessageDigest under Padding under Signature under PrivateKey - INode oidNode1 = messageDigestNode.getChildren().get(Oid.class); - assertThat(oidNode1).isNotNull(); - assertThat(oidNode1.getChildren()).isEmpty(); - assertThat(oidNode1.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); - - // Digest under MessageDigest under Padding under Signature under PrivateKey - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - - // BlockSize under MessageDigest under Padding under Signature under PrivateKey - INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("512"); - - // DigestSize under MessageDigest under Padding under Signature under PrivateKey - INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); - assertThat(digestSizeNode).isNotNull(); - assertThat(digestSizeNode.getChildren()).isEmpty(); - assertThat(digestSizeNode.asString()).isEqualTo("256"); - - // MaskGenerationFunction under Padding under Signature under PrivateKey - INode maskGenerationFunctionNode = - paddingNode.getChildren().get(MaskGenerationFunction.class); - assertThat(maskGenerationFunctionNode).isNotNull(); - assertThat(maskGenerationFunctionNode.getChildren()).hasSize(2); - assertThat(maskGenerationFunctionNode.asString()).isEqualTo("MGF1"); - - // Oid under MaskGenerationFunction under Padding under Signature under PrivateKey - INode oidNode2 = maskGenerationFunctionNode.getChildren().get(Oid.class); - assertThat(oidNode2).isNotNull(); - assertThat(oidNode2.getChildren()).isEmpty(); - assertThat(oidNode2.asString()).isEqualTo("1.2.840.113549.1.1.8"); - - // MessageDigest under MaskGenerationFunction under Padding under Signature under - // PrivateKey - INode messageDigestNode1 = - maskGenerationFunctionNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode1).isNotNull(); - assertThat(messageDigestNode1.getChildren()).hasSize(4); - assertThat(messageDigestNode1.asString()).isEqualTo("SHA-384"); - - // Oid under MessageDigest under MaskGenerationFunction under Padding under Signature - // under PrivateKey - INode oidNode3 = messageDigestNode1.getChildren().get(Oid.class); - assertThat(oidNode3).isNotNull(); - assertThat(oidNode3.getChildren()).isEmpty(); - assertThat(oidNode3.asString()).isEqualTo("2.16.840.1.101.3.4.2.2"); - - // Digest under MessageDigest under MaskGenerationFunction under Padding under Signature - // under PrivateKey - INode digestNode1 = messageDigestNode1.getChildren().get(Digest.class); - assertThat(digestNode1).isNotNull(); - assertThat(digestNode1.getChildren()).isEmpty(); - assertThat(digestNode1.asString()).isEqualTo("DIGEST"); - - // BlockSize under MessageDigest under MaskGenerationFunction under Padding under - // Signature under PrivateKey - INode blockSizeNode1 = messageDigestNode1.getChildren().get(BlockSize.class); - assertThat(blockSizeNode1).isNotNull(); - assertThat(blockSizeNode1.getChildren()).isEmpty(); - assertThat(blockSizeNode1.asString()).isEqualTo("1024"); - - // DigestSize under MessageDigest under MaskGenerationFunction under Padding under - // Signature under PrivateKey - INode digestSizeNode1 = messageDigestNode1.getChildren().get(DigestSize.class); - assertThat(digestSizeNode1).isNotNull(); - assertThat(digestSizeNode1.getChildren()).isEmpty(); - assertThat(digestSizeNode1.asString()).isEqualTo("384"); - - // Decrypt under PrivateKey - INode decryptNode = privateKeyNode.getChildren().get(Decrypt.class); - assertThat(decryptNode).isNotNull(); - assertThat(decryptNode.getChildren()).isEmpty(); - assertThat(decryptNode.asString()).isEqualTo("DECRYPT"); - - // KeyLength under PrivateKey - INode keyLengthNode = privateKeyNode.getChildren().get(KeyLength.class); - assertThat(keyLengthNode).isNotNull(); - assertThat(keyLengthNode.getChildren()).isEmpty(); - assertThat(keyLengthNode.asString()).isEqualTo("1024"); - } - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSANumbersTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSANumbersTest.java deleted file mode 100644 index c78a44f64..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSANumbersTest.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.asymmetric.RSA; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeyAction; -import com.ibm.engine.model.context.PrivateKeyContext; -import com.ibm.engine.model.context.PublicKeyContext; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.PrivateKey; -import com.ibm.mapper.model.PublicKey; -import com.ibm.mapper.model.PublicKeyEncryption; -import com.ibm.mapper.model.functionality.KeyGeneration; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaRSANumbersTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/RSA/PycaRSANumbersTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - - if (findingId == 0) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()) - .isInstanceOf(PublicKeyContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(KeyAction.class); - assertThat(value0.asString()).isEqualTo("GENERATION"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // PublicKey - INode publicKeyNode = nodes.get(0); - assertThat(publicKeyNode.getKind()).isEqualTo(PublicKey.class); - assertThat(publicKeyNode.getChildren()).hasSize(2); - assertThat(publicKeyNode.asString()).isEqualTo("RSA"); - - // PublicKeyEncryption under PublicKey - INode publicKeyEncryptionNode = - publicKeyNode.getChildren().get(PublicKeyEncryption.class); - assertThat(publicKeyEncryptionNode).isNotNull(); - assertThat(publicKeyEncryptionNode.getChildren()).hasSize(1); - assertThat(publicKeyEncryptionNode.asString()).isEqualTo("RSA"); - - // Oid under PublicKeyEncryption under PublicKey - INode oidNode = publicKeyEncryptionNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("1.2.840.113549.1.1.1"); - - // KeyGeneration under PublicKey - INode keyGenerationNode = publicKeyNode.getChildren().get(KeyGeneration.class); - assertThat(keyGenerationNode).isNotNull(); - assertThat(keyGenerationNode.getChildren()).isEmpty(); - assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); - } else if (findingId == 1) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()) - .isInstanceOf(PrivateKeyContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(KeyAction.class); - assertThat(value0.asString()).isEqualTo("GENERATION"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // PrivateKey - INode privateKeyNode = nodes.get(0); - assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); - assertThat(privateKeyNode.getChildren()).hasSize(2); - assertThat(privateKeyNode.asString()).isEqualTo("RSA"); - - // PublicKeyEncryption under PrivateKey - INode publicKeyEncryptionNode = - privateKeyNode.getChildren().get(PublicKeyEncryption.class); - assertThat(publicKeyEncryptionNode).isNotNull(); - assertThat(publicKeyEncryptionNode.getChildren()).hasSize(1); - assertThat(publicKeyEncryptionNode.asString()).isEqualTo("RSA"); - - // Oid under PublicKeyEncryption under PrivateKey - INode oidNode = publicKeyEncryptionNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("1.2.840.113549.1.1.1"); - - // KeyGeneration under PrivateKey - INode keyGenerationNode = privateKeyNode.getChildren().get(KeyGeneration.class); - assertThat(keyGenerationNode).isNotNull(); - assertThat(keyGenerationNode.getChildren()).isEmpty(); - assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); - } - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSASign1Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSASign1Test.java deleted file mode 100644 index e57bdb78a..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSASign1Test.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.asymmetric.RSA; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeySize; -import com.ibm.engine.model.SignatureAction; -import com.ibm.engine.model.ValueAction; -import com.ibm.engine.model.context.DigestContext; -import com.ibm.engine.model.context.PrivateKeyContext; -import com.ibm.engine.model.context.SignatureContext; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.DigestSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.KeyLength; -import com.ibm.mapper.model.MaskGenerationFunction; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.PrivateKey; -import com.ibm.mapper.model.ProbabilisticSignatureScheme; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.mapper.model.functionality.KeyGeneration; -import com.ibm.mapper.model.functionality.Sign; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaRSASign1Test extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/RSA/PycaRSASign1TestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(PrivateKeyContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(KeySize.class); - assertThat(value0.asString()).isEqualTo("2048"); - - DetectionStore store_1 = - getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); - assertThat(store_1.getDetectionValues()).hasSize(1); - assertThat(store_1.getDetectionValueContext()).isInstanceOf(SignatureContext.class); - IValue value0_1 = store_1.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(SignatureAction.class); - assertThat(value0_1.asString()).isEqualTo("SIGN"); - - DetectionStore store_1_1 = - getStoreOfValueType(ValueAction.class, store_1.getChildren()); - assertThat(store_1_1.getDetectionValues()).hasSize(1); - assertThat(store_1_1.getDetectionValueContext()).isInstanceOf(SignatureContext.class); - IValue value0_1_1 = store_1_1.getDetectionValues().get(0); - assertThat(value0_1_1).isInstanceOf(ValueAction.class); - assertThat(value0_1_1.asString()).isEqualTo("RSA-PSS"); - - DetectionStore store_1_1_1 = - getStoreOfValueType(ValueAction.class, store_1_1.getChildren()); - assertThat(store_1_1_1.getDetectionValues()).hasSize(1); - assertThat(store_1_1_1.getDetectionValueContext()).isInstanceOf(SignatureContext.class); - IValue value0_1_1_1 = store_1_1_1.getDetectionValues().get(0); - assertThat(value0_1_1_1).isInstanceOf(ValueAction.class); - assertThat(value0_1_1_1.asString()).isEqualTo("MGF1"); - - DetectionStore store_1_1_1_1 = - getStoreOfValueType(ValueAction.class, store_1_1_1.getChildren()); - assertThat(store_1_1_1_1.getDetectionValues()).hasSize(1); - assertThat(store_1_1_1_1.getDetectionValueContext()).isInstanceOf(DigestContext.class); - IValue value0_1_1_1_1 = store_1_1_1_1.getDetectionValues().get(0); - assertThat(value0_1_1_1_1).isInstanceOf(ValueAction.class); - assertThat(value0_1_1_1_1.asString()).isEqualTo("SHA256"); - - /* - * Translation - */ - - assertThat(nodes).hasSize(1); - - // PrivateKey - INode privateKeyNode = nodes.get(0); - assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); - assertThat(privateKeyNode.getChildren()).hasSize(4); - assertThat(privateKeyNode.asString()).isEqualTo("RSA"); - - // Sign under PrivateKey - INode signNode = privateKeyNode.getChildren().get(Sign.class); - assertThat(signNode).isNotNull(); - assertThat(signNode.getChildren()).isEmpty(); - assertThat(signNode.asString()).isEqualTo("SIGN"); - - // KeyGeneration under PrivateKey - INode keyGenerationNode = privateKeyNode.getChildren().get(KeyGeneration.class); - assertThat(keyGenerationNode).isNotNull(); - assertThat(keyGenerationNode.getChildren()).isEmpty(); - assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); - - // ProbabilisticSignatureScheme under PrivateKey - INode probabilisticSignatureSchemeNode = - privateKeyNode.getChildren().get(ProbabilisticSignatureScheme.class); - assertThat(probabilisticSignatureSchemeNode).isNotNull(); - assertThat(probabilisticSignatureSchemeNode.getChildren()).hasSize(3); - assertThat(probabilisticSignatureSchemeNode.asString()).isEqualTo("RSA-PSS"); - - // MaskGenerationFunction under ProbabilisticSignatureScheme under PrivateKey - INode maskGenerationFunctionNode = - probabilisticSignatureSchemeNode.getChildren().get(MaskGenerationFunction.class); - assertThat(maskGenerationFunctionNode).isNotNull(); - assertThat(maskGenerationFunctionNode.getChildren()).hasSize(2); - assertThat(maskGenerationFunctionNode.asString()).isEqualTo("MGF1"); - - // MessageDigest under MaskGenerationFunction under ProbabilisticSignatureScheme under - // PrivateKey - INode messageDigestNode = maskGenerationFunctionNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(4); - assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); - - // BlockSize under MessageDigest under MaskGenerationFunction under - // ProbabilisticSignatureScheme under PrivateKey - INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("512"); - - // DigestSize under MessageDigest under MaskGenerationFunction under - // ProbabilisticSignatureScheme under PrivateKey - INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); - assertThat(digestSizeNode).isNotNull(); - assertThat(digestSizeNode.getChildren()).isEmpty(); - assertThat(digestSizeNode.asString()).isEqualTo("256"); - - // Oid under MessageDigest under MaskGenerationFunction under ProbabilisticSignatureScheme - // under PrivateKey - INode oidNode1 = messageDigestNode.getChildren().get(Oid.class); - assertThat(oidNode1).isNotNull(); - assertThat(oidNode1.getChildren()).isEmpty(); - assertThat(oidNode1.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); - - // Digest under MessageDigest under MaskGenerationFunction under - // ProbabilisticSignatureScheme under PrivateKey - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - - // Oid under MaskGenerationFunction under ProbabilisticSignatureScheme under PrivateKey - INode oidNode2 = maskGenerationFunctionNode.getChildren().get(Oid.class); - assertThat(oidNode2).isNotNull(); - assertThat(oidNode2.getChildren()).isEmpty(); - assertThat(oidNode2.asString()).isEqualTo("1.2.840.113549.1.1.8"); - - // MessageDigest under ProbabilisticSignatureScheme under PrivateKey - INode messageDigestNode1 = - probabilisticSignatureSchemeNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode1).isNotNull(); - assertThat(messageDigestNode1.getChildren()).hasSize(4); - assertThat(messageDigestNode1.asString()).isEqualTo("SHA-384"); - - // BlockSize under MessageDigest under ProbabilisticSignatureScheme under PrivateKey - INode blockSizeNode1 = messageDigestNode1.getChildren().get(BlockSize.class); - assertThat(blockSizeNode1).isNotNull(); - assertThat(blockSizeNode1.getChildren()).isEmpty(); - assertThat(blockSizeNode1.asString()).isEqualTo("1024"); - - // DigestSize under MessageDigest under ProbabilisticSignatureScheme under PrivateKey - INode digestSizeNode1 = messageDigestNode1.getChildren().get(DigestSize.class); - assertThat(digestSizeNode1).isNotNull(); - assertThat(digestSizeNode1.getChildren()).isEmpty(); - assertThat(digestSizeNode1.asString()).isEqualTo("384"); - - // Oid under MessageDigest under ProbabilisticSignatureScheme under PrivateKey - INode oidNode3 = messageDigestNode1.getChildren().get(Oid.class); - assertThat(oidNode3).isNotNull(); - assertThat(oidNode3.getChildren()).isEmpty(); - assertThat(oidNode3.asString()).isEqualTo("2.16.840.1.101.3.4.2.2"); - - // Digest under MessageDigest under ProbabilisticSignatureScheme under PrivateKey - INode digestNode1 = messageDigestNode1.getChildren().get(Digest.class); - assertThat(digestNode1).isNotNull(); - assertThat(digestNode1.getChildren()).isEmpty(); - assertThat(digestNode1.asString()).isEqualTo("DIGEST"); - - // Oid under ProbabilisticSignatureScheme under PrivateKey - INode oidNode4 = probabilisticSignatureSchemeNode.getChildren().get(Oid.class); - assertThat(oidNode4).isNotNull(); - assertThat(oidNode4.getChildren()).isEmpty(); - assertThat(oidNode4.asString()).isEqualTo("1.2.840.113549.1.1.10"); - - // KeyLength under PrivateKey - INode keyLengthNode = privateKeyNode.getChildren().get(KeyLength.class); - assertThat(keyLengthNode).isNotNull(); - assertThat(keyLengthNode.getChildren()).isEmpty(); - assertThat(keyLengthNode.asString()).isEqualTo("2048"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSASign2Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSASign2Test.java deleted file mode 100644 index bb5181e56..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSASign2Test.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.asymmetric.RSA; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeySize; -import com.ibm.engine.model.SignatureAction; -import com.ibm.engine.model.ValueAction; -import com.ibm.engine.model.context.DigestContext; -import com.ibm.engine.model.context.PrivateKeyContext; -import com.ibm.engine.model.context.SignatureContext; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.DigestSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.KeyLength; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.PrivateKey; -import com.ibm.mapper.model.Signature; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.mapper.model.functionality.KeyGeneration; -import com.ibm.mapper.model.functionality.Sign; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaRSASign2Test extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/RSA/PycaRSASign2TestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(PrivateKeyContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(KeySize.class); - assertThat(value0.asString()).isEqualTo("2048"); - - DetectionStore store_1 = - getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); - assertThat(store_1.getDetectionValues()).hasSize(1); - assertThat(store_1.getDetectionValueContext()).isInstanceOf(SignatureContext.class); - IValue value0_1 = store_1.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(SignatureAction.class); - assertThat(value0_1.asString()).isEqualTo("SIGN"); - - List> stores = - getStoresOfValueType(ValueAction.class, store_1.getChildren()); - assertThat(stores).isNotNull(); - for (DetectionStore store : stores) { - assertThat(store.getDetectionValues()).hasSize(1); - assertThat(store.getDetectionValueContext()) - .isInstanceOfAny(SignatureContext.class, DigestContext.class); - IValue v = store.getDetectionValues().get(0); - assertThat(v).isInstanceOf(ValueAction.class); - assertThat(v.asString()) - .satisfiesAnyOf( - s -> assertThat(s).isEqualTo("PKCS1v15"), - s -> assertThat(s).isEqualTo("SHA3_384")); - } - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // PrivateKey - INode privateKeyNode = nodes.get(0); - assertThat(privateKeyNode.getKind()).isEqualTo(PrivateKey.class); - assertThat(privateKeyNode.getChildren()).hasSize(4); - assertThat(privateKeyNode.asString()).isEqualTo("RSA"); - - // KeyGeneration under PrivateKey - INode keyGenerationNode = privateKeyNode.getChildren().get(KeyGeneration.class); - assertThat(keyGenerationNode).isNotNull(); - assertThat(keyGenerationNode.getChildren()).isEmpty(); - assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); - - // Sign under PrivateKey - INode signNode = privateKeyNode.getChildren().get(Sign.class); - assertThat(signNode).isNotNull(); - assertThat(signNode.getChildren()).isEmpty(); - assertThat(signNode.asString()).isEqualTo("SIGN"); - - // KeyLength under PrivateKey - INode keyLengthNode = privateKeyNode.getChildren().get(KeyLength.class); - assertThat(keyLengthNode).isNotNull(); - assertThat(keyLengthNode.getChildren()).isEmpty(); - assertThat(keyLengthNode.asString()).isEqualTo("2048"); - - // Signature under PrivateKey - INode signatureNode = privateKeyNode.getChildren().get(Signature.class); - assertThat(signatureNode).isNotNull(); - assertThat(signatureNode.getChildren()).hasSize(2); - assertThat(signatureNode.asString()).isEqualTo("RSA-PKCS1-1.5-SHA3-384"); - - // MessageDigest under Signature under PrivateKey - INode messageDigestNode = signatureNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(4); - assertThat(messageDigestNode.asString()).isEqualTo("SHA3-384"); - - // DigestSize under MessageDigest under Signature under PrivateKey - INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); - assertThat(digestSizeNode).isNotNull(); - assertThat(digestSizeNode.getChildren()).isEmpty(); - assertThat(digestSizeNode.asString()).isEqualTo("384"); - - // Oid under MessageDigest under Signature under PrivateKey - INode oidNode = messageDigestNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.9"); - - // BlockSize under MessageDigest under Signature under PrivateKey - INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("832"); - - // Digest under MessageDigest under Signature under PrivateKey - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - - // Oid under Signature under PrivateKey - INode oidNode1 = signatureNode.getChildren().get(Oid.class); - assertThat(oidNode1).isNotNull(); - assertThat(oidNode1.getChildren()).isEmpty(); - assertThat(oidNode1.asString()).isEqualTo("2.16.840.1.101.3.4.3.15"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/fernet/PycaFernetDecryptTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/fernet/PycaFernetDecryptTest.java deleted file mode 100644 index 10119414d..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/fernet/PycaFernetDecryptTest.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.fernet; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.CipherAction; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeyAction; -import com.ibm.engine.model.context.CipherContext; -import com.ibm.engine.model.context.KeyContext; -import com.ibm.mapper.model.AuthenticatedEncryption; -import com.ibm.mapper.model.BlockCipher; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.DigestSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.KeyLength; -import com.ibm.mapper.model.Mac; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.Mode; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.Padding; -import com.ibm.mapper.model.SecretKey; -import com.ibm.mapper.model.functionality.Decrypt; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.mapper.model.functionality.KeyGeneration; -import com.ibm.mapper.model.functionality.Tag; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaFernetDecryptTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/fernet/PycaFernetDecryptTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(KeyContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(KeyAction.class); - assertThat(value0.asString()).isEqualTo("GENERATION"); - - DetectionStore store_1 = - getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); - assertThat(store_1.getDetectionValues()).hasSize(1); - assertThat(store_1.getDetectionValueContext()).isInstanceOf(CipherContext.class); - IValue value0_1 = store_1.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(CipherAction.class); - assertThat(value0_1.asString()).isEqualTo("DECRYPT"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // SecretKey - INode secretKeyNode = nodes.get(0); - assertThat(secretKeyNode.getKind()).isEqualTo(SecretKey.class); - assertThat(secretKeyNode.getChildren()).hasSize(3); - assertThat(secretKeyNode.asString()).isEqualTo("Fernet"); - - // KeyGeneration under SecretKey - INode keyGenerationNode = secretKeyNode.getChildren().get(KeyGeneration.class); - assertThat(keyGenerationNode).isNotNull(); - assertThat(keyGenerationNode.getChildren()).isEmpty(); - assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); - - // Decrypt under SecretKey - INode decryptNode = secretKeyNode.getChildren().get(Decrypt.class); - assertThat(decryptNode).isNotNull(); - assertThat(decryptNode.getChildren()).isEmpty(); - assertThat(decryptNode.asString()).isEqualTo("DECRYPT"); - - // AuthenticatedEncryption under SecretKey - INode authenticatedEncryptionNode = - secretKeyNode.getChildren().get(AuthenticatedEncryption.class); - assertThat(authenticatedEncryptionNode).isNotNull(); - assertThat(authenticatedEncryptionNode.getChildren()).hasSize(2); - assertThat(authenticatedEncryptionNode.asString()).isEqualTo("Fernet"); - - // Mac under AuthenticatedEncryption under SecretKey - INode macNode = authenticatedEncryptionNode.getChildren().get(Mac.class); - assertThat(macNode).isNotNull(); - assertThat(macNode.getChildren()).hasSize(3); - assertThat(macNode.asString()).isEqualTo("HMAC-SHA-256"); - - // MessageDigest under Mac under AuthenticatedEncryption under SecretKey - INode messageDigestNode = macNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(4); - assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); - - // Digest under MessageDigest under Mac under AuthenticatedEncryption under SecretKey - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - - // BlockSize under MessageDigest under Mac under AuthenticatedEncryption under SecretKey - INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("512"); - - // DigestSize under MessageDigest under Mac under AuthenticatedEncryption under SecretKey - INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); - assertThat(digestSizeNode).isNotNull(); - assertThat(digestSizeNode.getChildren()).isEmpty(); - assertThat(digestSizeNode.asString()).isEqualTo("256"); - - // Oid under MessageDigest under Mac under AuthenticatedEncryption under SecretKey - INode oidNode = messageDigestNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); - - // Tag under Mac under AuthenticatedEncryption under SecretKey - INode tagNode = macNode.getChildren().get(Tag.class); - assertThat(tagNode).isNotNull(); - assertThat(tagNode.getChildren()).isEmpty(); - assertThat(tagNode.asString()).isEqualTo("TAG"); - - // Oid under Mac under AuthenticatedEncryption under SecretKey - INode oidNode1 = macNode.getChildren().get(Oid.class); - assertThat(oidNode1).isNotNull(); - assertThat(oidNode1.getChildren()).isEmpty(); - assertThat(oidNode1.asString()).isEqualTo("1.2.840.113549.2.9"); - - // BlockCipher under AuthenticatedEncryption under SecretKey - INode blockCipherNode = authenticatedEncryptionNode.getChildren().get(BlockCipher.class); - assertThat(blockCipherNode).isNotNull(); - assertThat(blockCipherNode.getChildren()).hasSize(5); - assertThat(blockCipherNode.asString()).isEqualTo("AES-128-CBC-PKCS7"); - - // Mode under BlockCipher under AuthenticatedEncryption under SecretKey - INode modeNode = blockCipherNode.getChildren().get(Mode.class); - assertThat(modeNode).isNotNull(); - assertThat(modeNode.getChildren()).isEmpty(); - assertThat(modeNode.asString()).isEqualTo("CBC"); - - // BlockSize under BlockCipher under AuthenticatedEncryption under SecretKey - INode blockSizeNode1 = blockCipherNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode1).isNotNull(); - assertThat(blockSizeNode1.getChildren()).isEmpty(); - assertThat(blockSizeNode1.asString()).isEqualTo("128"); - - // Padding under BlockCipher under AuthenticatedEncryption under SecretKey - INode paddingNode = blockCipherNode.getChildren().get(Padding.class); - assertThat(paddingNode).isNotNull(); - assertThat(paddingNode.getChildren()).isEmpty(); - assertThat(paddingNode.asString()).isEqualTo("PKCS7"); - - // KeyLength under BlockCipher under AuthenticatedEncryption under SecretKey - INode keyLengthNode = blockCipherNode.getChildren().get(KeyLength.class); - assertThat(keyLengthNode).isNotNull(); - assertThat(keyLengthNode.getChildren()).isEmpty(); - assertThat(keyLengthNode.asString()).isEqualTo("128"); - - // Oid under BlockCipher under AuthenticatedEncryption under SecretKey - INode oidNode2 = blockCipherNode.getChildren().get(Oid.class); - assertThat(oidNode2).isNotNull(); - assertThat(oidNode2.getChildren()).isEmpty(); - assertThat(oidNode2.asString()).isEqualTo("2.16.840.1.101.3.4.1.2"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/fernet/PycaFernetEncryptTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/fernet/PycaFernetEncryptTest.java deleted file mode 100644 index 759916b45..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/fernet/PycaFernetEncryptTest.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.fernet; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.CipherAction; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeyAction; -import com.ibm.engine.model.context.CipherContext; -import com.ibm.engine.model.context.KeyContext; -import com.ibm.mapper.model.AuthenticatedEncryption; -import com.ibm.mapper.model.BlockCipher; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.DigestSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.KeyLength; -import com.ibm.mapper.model.Mac; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.Mode; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.Padding; -import com.ibm.mapper.model.SecretKey; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.mapper.model.functionality.Encrypt; -import com.ibm.mapper.model.functionality.KeyGeneration; -import com.ibm.mapper.model.functionality.Tag; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaFernetEncryptTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/fernet/PycaFernetEncryptTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(KeyContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(KeyAction.class); - assertThat(value0.asString()).isEqualTo("GENERATION"); - - DetectionStore store_1 = - getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); - assertThat(store_1.getDetectionValues()).hasSize(1); - assertThat(store_1.getDetectionValueContext()).isInstanceOf(CipherContext.class); - IValue value0_1 = store_1.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(CipherAction.class); - assertThat(value0_1.asString()).isEqualTo("ENCRYPT"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // SecretKey - INode secretKeyNode = nodes.get(0); - assertThat(secretKeyNode.getKind()).isEqualTo(SecretKey.class); - assertThat(secretKeyNode.getChildren()).hasSize(3); - assertThat(secretKeyNode.asString()).isEqualTo("Fernet"); - - // Encrypt under SecretKey - INode encryptNode = secretKeyNode.getChildren().get(Encrypt.class); - assertThat(encryptNode).isNotNull(); - assertThat(encryptNode.getChildren()).isEmpty(); - assertThat(encryptNode.asString()).isEqualTo("ENCRYPT"); - - // KeyGeneration under SecretKey - INode keyGenerationNode = secretKeyNode.getChildren().get(KeyGeneration.class); - assertThat(keyGenerationNode).isNotNull(); - assertThat(keyGenerationNode.getChildren()).isEmpty(); - assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); - - // AuthenticatedEncryption under SecretKey - INode authenticatedEncryptionNode = - secretKeyNode.getChildren().get(AuthenticatedEncryption.class); - assertThat(authenticatedEncryptionNode).isNotNull(); - assertThat(authenticatedEncryptionNode.getChildren()).hasSize(2); - assertThat(authenticatedEncryptionNode.asString()).isEqualTo("Fernet"); - - // Mac under AuthenticatedEncryption under SecretKey - INode macNode = authenticatedEncryptionNode.getChildren().get(Mac.class); - assertThat(macNode).isNotNull(); - assertThat(macNode.getChildren()).hasSize(3); - assertThat(macNode.asString()).isEqualTo("HMAC-SHA-256"); - - // MessageDigest under Mac under AuthenticatedEncryption under SecretKey - INode messageDigestNode = macNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(4); - assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); - - // Digest under MessageDigest under Mac under AuthenticatedEncryption under SecretKey - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - - // Oid under MessageDigest under Mac under AuthenticatedEncryption under SecretKey - INode oidNode = messageDigestNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); - - // BlockSize under MessageDigest under Mac under AuthenticatedEncryption under SecretKey - INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("512"); - - // DigestSize under MessageDigest under Mac under AuthenticatedEncryption under SecretKey - INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); - assertThat(digestSizeNode).isNotNull(); - assertThat(digestSizeNode.getChildren()).isEmpty(); - assertThat(digestSizeNode.asString()).isEqualTo("256"); - - // Tag under Mac under AuthenticatedEncryption under SecretKey - INode tagNode = macNode.getChildren().get(Tag.class); - assertThat(tagNode).isNotNull(); - assertThat(tagNode.getChildren()).isEmpty(); - assertThat(tagNode.asString()).isEqualTo("TAG"); - - // Oid under Mac under AuthenticatedEncryption under SecretKey - INode oidNode1 = macNode.getChildren().get(Oid.class); - assertThat(oidNode1).isNotNull(); - assertThat(oidNode1.getChildren()).isEmpty(); - assertThat(oidNode1.asString()).isEqualTo("1.2.840.113549.2.9"); - - // BlockCipher under AuthenticatedEncryption under SecretKey - INode blockCipherNode = authenticatedEncryptionNode.getChildren().get(BlockCipher.class); - assertThat(blockCipherNode).isNotNull(); - assertThat(blockCipherNode.getChildren()).hasSize(5); - assertThat(blockCipherNode.asString()).isEqualTo("AES-128-CBC-PKCS7"); - - // Mode under BlockCipher under AuthenticatedEncryption under SecretKey - INode modeNode = blockCipherNode.getChildren().get(Mode.class); - assertThat(modeNode).isNotNull(); - assertThat(modeNode.getChildren()).isEmpty(); - assertThat(modeNode.asString()).isEqualTo("CBC"); - - // Padding under BlockCipher under AuthenticatedEncryption under SecretKey - INode paddingNode = blockCipherNode.getChildren().get(Padding.class); - assertThat(paddingNode).isNotNull(); - assertThat(paddingNode.getChildren()).isEmpty(); - assertThat(paddingNode.asString()).isEqualTo("PKCS7"); - - // KeyLength under BlockCipher under AuthenticatedEncryption under SecretKey - INode keyLengthNode = blockCipherNode.getChildren().get(KeyLength.class); - assertThat(keyLengthNode).isNotNull(); - assertThat(keyLengthNode.getChildren()).isEmpty(); - assertThat(keyLengthNode.asString()).isEqualTo("128"); - - // Oid under BlockCipher under AuthenticatedEncryption under SecretKey - INode oidNode2 = blockCipherNode.getChildren().get(Oid.class); - assertThat(oidNode2).isNotNull(); - assertThat(oidNode2.getChildren()).isEmpty(); - assertThat(oidNode2.asString()).isEqualTo("2.16.840.1.101.3.4.1.2"); - - // BlockSize under BlockCipher under AuthenticatedEncryption under SecretKey - INode blockSizeNode1 = blockCipherNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode1).isNotNull(); - assertThat(blockSizeNode1.getChildren()).isEmpty(); - assertThat(blockSizeNode1.asString()).isEqualTo("128"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/fernet/PycaMultiFernetTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/fernet/PycaMultiFernetTest.java deleted file mode 100644 index 36fe6893b..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/fernet/PycaMultiFernetTest.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.fernet; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.CipherAction; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeyAction; -import com.ibm.engine.model.context.CipherContext; -import com.ibm.engine.model.context.KeyContext; -import com.ibm.mapper.model.AuthenticatedEncryption; -import com.ibm.mapper.model.BlockCipher; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.DigestSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.KeyLength; -import com.ibm.mapper.model.Mac; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.Mode; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.Padding; -import com.ibm.mapper.model.SecretKey; -import com.ibm.mapper.model.functionality.Decrypt; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.mapper.model.functionality.Encrypt; -import com.ibm.mapper.model.functionality.KeyGeneration; -import com.ibm.mapper.model.functionality.Tag; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaMultiFernetTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/fernet/PycaMultiFernetTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(KeyContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(KeyAction.class); - assertThat(value0.asString()).isEqualTo("GENERATION"); - - DetectionStore store_2 = - getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); - assertThat(store_2.getDetectionValues()).hasSize(1); - assertThat(store_2.getDetectionValueContext()).isInstanceOf(CipherContext.class); - IValue value0_2 = store_2.getDetectionValues().get(0); - assertThat(value0_2).isInstanceOf(CipherAction.class); - assertThat(value0_2.asString()) - .satisfiesAnyOf( - s -> assertThat(s).isEqualTo("ENCRYPT"), - s -> assertThat(s).isEqualTo("DECRYPT")); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // SecretKey - INode secretKeyNode = nodes.get(0); - assertThat(secretKeyNode.getKind()).isEqualTo(SecretKey.class); - assertThat(secretKeyNode.getChildren()).hasSize(4); - assertThat(secretKeyNode.asString()).isEqualTo("Fernet"); - - // Decrypt under SecretKey - INode decryptNode = secretKeyNode.getChildren().get(Decrypt.class); - assertThat(decryptNode).isNotNull(); - assertThat(decryptNode.getChildren()).isEmpty(); - assertThat(decryptNode.asString()).isEqualTo("DECRYPT"); - - // AuthenticatedEncryption under SecretKey - INode authenticatedEncryptionNode = - secretKeyNode.getChildren().get(AuthenticatedEncryption.class); - assertThat(authenticatedEncryptionNode).isNotNull(); - assertThat(authenticatedEncryptionNode.getChildren()).hasSize(2); - assertThat(authenticatedEncryptionNode.asString()).isEqualTo("Fernet"); - - // BlockCipher under AuthenticatedEncryption under SecretKey - INode blockCipherNode = authenticatedEncryptionNode.getChildren().get(BlockCipher.class); - assertThat(blockCipherNode).isNotNull(); - assertThat(blockCipherNode.getChildren()).hasSize(5); - assertThat(blockCipherNode.asString()).isEqualTo("AES-128-CBC-PKCS7"); - - // BlockSize under BlockCipher under AuthenticatedEncryption under SecretKey - INode blockSizeNode = blockCipherNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("128"); - - // KeyLength under BlockCipher under AuthenticatedEncryption under SecretKey - INode keyLengthNode = blockCipherNode.getChildren().get(KeyLength.class); - assertThat(keyLengthNode).isNotNull(); - assertThat(keyLengthNode.getChildren()).isEmpty(); - assertThat(keyLengthNode.asString()).isEqualTo("128"); - - // Oid under BlockCipher under AuthenticatedEncryption under SecretKey - INode oidNode = blockCipherNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.1.2"); - - // Mode under BlockCipher under AuthenticatedEncryption under SecretKey - INode modeNode = blockCipherNode.getChildren().get(Mode.class); - assertThat(modeNode).isNotNull(); - assertThat(modeNode.getChildren()).isEmpty(); - assertThat(modeNode.asString()).isEqualTo("CBC"); - - // Padding under BlockCipher under AuthenticatedEncryption under SecretKey - INode paddingNode = blockCipherNode.getChildren().get(Padding.class); - assertThat(paddingNode).isNotNull(); - assertThat(paddingNode.getChildren()).isEmpty(); - assertThat(paddingNode.asString()).isEqualTo("PKCS7"); - - // Mac under AuthenticatedEncryption under SecretKey - INode macNode = authenticatedEncryptionNode.getChildren().get(Mac.class); - assertThat(macNode).isNotNull(); - assertThat(macNode.getChildren()).hasSize(3); - assertThat(macNode.asString()).isEqualTo("HMAC-SHA-256"); - - // Tag under Mac under AuthenticatedEncryption under SecretKey - INode tagNode = macNode.getChildren().get(Tag.class); - assertThat(tagNode).isNotNull(); - assertThat(tagNode.getChildren()).isEmpty(); - assertThat(tagNode.asString()).isEqualTo("TAG"); - - // Oid under Mac under AuthenticatedEncryption under SecretKey - oidNode = macNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("1.2.840.113549.2.9"); - - // MessageDigest under Mac under AuthenticatedEncryption under SecretKey - INode messageDigestNode = macNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(4); - assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); - - // BlockSize under MessageDigest under Mac under AuthenticatedEncryption under SecretKey - INode blockSizeNode1 = messageDigestNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode1).isNotNull(); - assertThat(blockSizeNode1.getChildren()).isEmpty(); - assertThat(blockSizeNode1.asString()).isEqualTo("512"); - - // Oid under MessageDigest under Mac under AuthenticatedEncryption under SecretKey - INode oidNode1 = messageDigestNode.getChildren().get(Oid.class); - assertThat(oidNode1).isNotNull(); - assertThat(oidNode1.getChildren()).isEmpty(); - assertThat(oidNode1.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); - - // DigestSize under MessageDigest under Mac under AuthenticatedEncryption under - // SecretKey - INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); - assertThat(digestSizeNode).isNotNull(); - assertThat(digestSizeNode.getChildren()).isEmpty(); - assertThat(digestSizeNode.asString()).isEqualTo("256"); - - // Digest under MessageDigest under Mac under AuthenticatedEncryption under SecretKey - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - - // Encrypt under SecretKey - INode encryptNode = secretKeyNode.getChildren().get(Encrypt.class); - assertThat(encryptNode).isNotNull(); - assertThat(encryptNode.getChildren()).isEmpty(); - assertThat(encryptNode.asString()).isEqualTo("ENCRYPT"); - - // KeyGeneration under SecretKey - INode keyGenerationNode = secretKeyNode.getChildren().get(KeyGeneration.class); - assertThat(keyGenerationNode).isNotNull(); - assertThat(keyGenerationNode.getChildren()).isEmpty(); - assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/hash/PycaHashDirectTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/hash/PycaHashDirectTest.java deleted file mode 100644 index 73cef79d4..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/hash/PycaHashDirectTest.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.hash; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.Algorithm; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.context.DigestContext; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.DigestSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaHashDirectTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/hash/PycaHashDirectTest.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(Algorithm.class); - assertThat(value0.asString()).isEqualTo("SHA256"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // MessageDigest (SHA256) - INode messageDigestNode = nodes.get(0); - assertThat(messageDigestNode.getKind()).isEqualTo(MessageDigest.class); - assertThat(messageDigestNode.getChildren()).hasSize(4); - assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); - - // DigestSize under MessageDigest - INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); - assertThat(digestSizeNode).isNotNull(); - assertThat(digestSizeNode.getChildren()).isEmpty(); - assertThat(digestSizeNode.asString()).isEqualTo("256"); - - // BlockSize under MessageDigest - INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("512"); - - // Oid under MessageDigest - INode oidNode = messageDigestNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); - - // Digest functionality under MessageDigest - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaConcatKDFHMACTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaConcatKDFHMACTest.java deleted file mode 100644 index 4c72b5be8..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaConcatKDFHMACTest.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.kdf; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.Algorithm; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeySize; -import com.ibm.engine.model.context.KeyDerivationFunctionContext; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.DigestSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.KeyDerivationFunction; -import com.ibm.mapper.model.KeyLength; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.mapper.model.functionality.KeyDerivation; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaConcatKDFHMACTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/kdf/PycaConcatKDFHMACTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(Algorithm.class); - assertThat(value0.asString()).isEqualTo("SHA256"); - - DetectionStore store_1 = - getStoreOfValueType(KeySize.class, detectionStore.getChildren()); - assertThat(store_1.getDetectionValues()).hasSize(1); - assertThat(store_1.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0_1 = store_1.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(KeySize.class); - assertThat(value0_1.asString()).isEqualTo("256"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // Mac - INode macNode = nodes.get(0); - assertThat(macNode.getKind()).isEqualTo(KeyDerivationFunction.class); - assertThat(macNode.getChildren()).hasSize(3); - assertThat(macNode.asString()).isEqualTo("ConcatenationKDF"); - - // MessageDigest under Mac - INode messageDigestNode = macNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(4); - assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); - - // DigestSize under MessageDigest under Mac - INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); - assertThat(digestSizeNode).isNotNull(); - assertThat(digestSizeNode.getChildren()).isEmpty(); - assertThat(digestSizeNode.asString()).isEqualTo("256"); - - // Oid under MessageDigest under Mac - INode oidNode = messageDigestNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); - - // Digest under MessageDigest under Mac - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - - // BlockSize under MessageDigest under Mac - INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("512"); - - // KeyDerivation under Mac - INode keyDerivationNode = macNode.getChildren().get(KeyDerivation.class); - assertThat(keyDerivationNode).isNotNull(); - assertThat(keyDerivationNode.getChildren()).isEmpty(); - assertThat(keyDerivationNode.asString()).isEqualTo("KEYDERIVATION"); - - // KeyLength under Mac - INode keyLengthNode = macNode.getChildren().get(KeyLength.class); - assertThat(keyLengthNode).isNotNull(); - assertThat(keyLengthNode.getChildren()).isEmpty(); - assertThat(keyLengthNode.asString()).isEqualTo("256"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaConcatKDFHashTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaConcatKDFHashTest.java deleted file mode 100644 index 68a47dbc9..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaConcatKDFHashTest.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.kdf; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.Algorithm; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeySize; -import com.ibm.engine.model.context.KeyDerivationFunctionContext; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.DigestSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.KeyDerivationFunction; -import com.ibm.mapper.model.KeyLength; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.mapper.model.functionality.KeyDerivation; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaConcatKDFHashTest extends TestBase { - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/kdf/PycaConcatKDFHashTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(Algorithm.class); - assertThat(value0.asString()).isEqualTo("SHA256"); - - DetectionStore store_1 = - getStoreOfValueType(KeySize.class, detectionStore.getChildren()); - assertThat(store_1.getDetectionValues()).hasSize(1); - assertThat(store_1.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0_1 = store_1.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(KeySize.class); - assertThat(value0_1.asString()).isEqualTo("512"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - // KeyDerivationFunction - INode keyDerivationFunctionNode = nodes.get(0); - assertThat(keyDerivationFunctionNode.getKind()).isEqualTo(KeyDerivationFunction.class); - assertThat(keyDerivationFunctionNode.getChildren()).hasSize(3); - assertThat(keyDerivationFunctionNode.asString()).isEqualTo("ConcatenationKDF"); - - // MessageDigest under KeyDerivationFunction - INode messageDigestNode = keyDerivationFunctionNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(4); - assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); - - // BlockSize under MessageDigest under KeyDerivationFunction - INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("512"); - - // Digest under MessageDigest under KeyDerivationFunction - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - - // Oid under MessageDigest under KeyDerivationFunction - INode oidNode = messageDigestNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); - - // DigestSize under MessageDigest under KeyDerivationFunction - INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); - assertThat(digestSizeNode).isNotNull(); - assertThat(digestSizeNode.getChildren()).isEmpty(); - assertThat(digestSizeNode.asString()).isEqualTo("256"); - - // KeyLength under KeyDerivationFunction - INode keyLengthNode = keyDerivationFunctionNode.getChildren().get(KeyLength.class); - assertThat(keyLengthNode).isNotNull(); - assertThat(keyLengthNode.getChildren()).isEmpty(); - assertThat(keyLengthNode.asString()).isEqualTo("512"); - - // KeyDerivation under KeyDerivationFunction - INode keyDerivationNode = keyDerivationFunctionNode.getChildren().get(KeyDerivation.class); - assertThat(keyDerivationNode).isNotNull(); - assertThat(keyDerivationNode.getChildren()).isEmpty(); - assertThat(keyDerivationNode.asString()).isEqualTo("KEYDERIVATION"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaHKDFExpandTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaHKDFExpandTest.java deleted file mode 100644 index 0eade96a6..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaHKDFExpandTest.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.kdf; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.Algorithm; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeySize; -import com.ibm.engine.model.context.KeyDerivationFunctionContext; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.DigestSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.KeyDerivationFunction; -import com.ibm.mapper.model.KeyLength; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.mapper.model.functionality.KeyDerivation; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaHKDFExpandTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/kdf/PycaHKDFExpandTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(Algorithm.class); - assertThat(value0.asString()).isEqualTo("SHA256"); - - DetectionStore store_1 = - getStoreOfValueType(KeySize.class, detectionStore.getChildren()); - assertThat(store_1.getDetectionValues()).hasSize(1); - assertThat(store_1.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0_1 = store_1.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(KeySize.class); - assertThat(value0_1.asString()).isEqualTo("256"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // KeyDerivationFunction - INode keyDerivationFunctionNode = nodes.get(0); - assertThat(keyDerivationFunctionNode.getKind()).isEqualTo(KeyDerivationFunction.class); - assertThat(keyDerivationFunctionNode.getChildren()).hasSize(3); - assertThat(keyDerivationFunctionNode.asString()).isEqualTo("HKDF-SHA-256"); - - // KeyDerivation under KeyDerivationFunction - INode keyDerivationNode = keyDerivationFunctionNode.getChildren().get(KeyDerivation.class); - assertThat(keyDerivationNode).isNotNull(); - assertThat(keyDerivationNode.getChildren()).isEmpty(); - assertThat(keyDerivationNode.asString()).isEqualTo("KEYDERIVATION"); - - // KeyLength under KeyDerivationFunction - INode keyLengthNode = keyDerivationFunctionNode.getChildren().get(KeyLength.class); - assertThat(keyLengthNode).isNotNull(); - assertThat(keyLengthNode.getChildren()).isEmpty(); - assertThat(keyLengthNode.asString()).isEqualTo("256"); - - // MessageDigest under KeyDerivationFunction - INode messageDigestNode = keyDerivationFunctionNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(4); - assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); - - // BlockSize under MessageDigest under KeyDerivationFunction - INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("512"); - - // Digest under MessageDigest under KeyDerivationFunction - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - - // DigestSize under MessageDigest under KeyDerivationFunction - INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); - assertThat(digestSizeNode).isNotNull(); - assertThat(digestSizeNode.getChildren()).isEmpty(); - assertThat(digestSizeNode.asString()).isEqualTo("256"); - - // Oid under MessageDigest under KeyDerivationFunction - INode oidNode = messageDigestNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaHKDFTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaHKDFTest.java deleted file mode 100644 index e7a4a5f16..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaHKDFTest.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.kdf; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.Algorithm; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeySize; -import com.ibm.engine.model.context.KeyDerivationFunctionContext; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.DigestSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.KeyDerivationFunction; -import com.ibm.mapper.model.KeyLength; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.mapper.model.functionality.KeyDerivation; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaHKDFTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify("src/test/files/rules/detection/kdf/PycaHKDFTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(Algorithm.class); - assertThat(value0.asString()).isEqualTo("SHA256"); - - DetectionStore store_1 = - getStoreOfValueType(KeySize.class, detectionStore.getChildren()); - assertThat(store_1.getDetectionValues()).hasSize(1); - assertThat(store_1.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0_1 = store_1.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(KeySize.class); - assertThat(value0_1.asString()).isEqualTo("256"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // KeyDerivationFunction - INode keyDerivationFunctionNode = nodes.get(0); - assertThat(keyDerivationFunctionNode.getKind()).isEqualTo(KeyDerivationFunction.class); - assertThat(keyDerivationFunctionNode.getChildren()).hasSize(3); - assertThat(keyDerivationFunctionNode.asString()).isEqualTo("HKDF-SHA-256"); - - // MessageDigest under KeyDerivationFunction - INode messageDigestNode = keyDerivationFunctionNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(4); - assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); - - // DigestSize under MessageDigest under KeyDerivationFunction - INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); - assertThat(digestSizeNode).isNotNull(); - assertThat(digestSizeNode.getChildren()).isEmpty(); - assertThat(digestSizeNode.asString()).isEqualTo("256"); - - // Oid under MessageDigest under KeyDerivationFunction - INode oidNode = messageDigestNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); - - // BlockSize under MessageDigest under KeyDerivationFunction - INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("512"); - - // Digest under MessageDigest under KeyDerivationFunction - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - - // KeyLength under KeyDerivationFunction - INode keyLengthNode = keyDerivationFunctionNode.getChildren().get(KeyLength.class); - assertThat(keyLengthNode).isNotNull(); - assertThat(keyLengthNode.getChildren()).isEmpty(); - assertThat(keyLengthNode.asString()).isEqualTo("256"); - - // KeyDerivation under KeyDerivationFunction - INode keyDerivationNode = keyDerivationFunctionNode.getChildren().get(KeyDerivation.class); - assertThat(keyDerivationNode).isNotNull(); - assertThat(keyDerivationNode.getChildren()).isEmpty(); - assertThat(keyDerivationNode.asString()).isEqualTo("KEYDERIVATION"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaKBKDFCMACTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaKBKDFCMACTest.java deleted file mode 100644 index 0436832c1..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaKBKDFCMACTest.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.kdf; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.Algorithm; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeySize; -import com.ibm.engine.model.context.KeyDerivationFunctionContext; -import com.ibm.mapper.model.BlockCipher; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.KeyLength; -import com.ibm.mapper.model.Mac; -import com.ibm.mapper.model.Mode; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.functionality.KeyDerivation; -import com.ibm.mapper.model.functionality.Tag; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaKBKDFCMACTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/kdf/PycaKBKDFCMACTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(Algorithm.class); - assertThat(value0.asString()).isEqualTo("AES"); - - DetectionStore store_1 = - getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); - assertThat(store_1.getDetectionValues()).hasSize(1); - assertThat(store_1.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0_1 = store_1.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(com.ibm.engine.model.Mode.class); - assertThat(value0_1.asString()).isEqualTo("CounterMode"); - - DetectionStore store_2 = - getStoreOfValueType(KeySize.class, detectionStore.getChildren()); - assertThat(store_2.getDetectionValues()).hasSize(1); - assertThat(store_2.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0_2 = store_2.getDetectionValues().get(0); - assertThat(value0_2).isInstanceOf(KeySize.class); - assertThat(value0_2.asString()).isEqualTo("256"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // Mac - INode macNode = nodes.get(0); - assertThat(macNode.getKind()).isEqualTo(Mac.class); - assertThat(macNode.getChildren()).hasSize(4); - assertThat(macNode.asString()).isEqualTo("CMAC-AES"); - - // Tag under Mac - INode tagNode = macNode.getChildren().get(Tag.class); - assertThat(tagNode).isNotNull(); - assertThat(tagNode.getChildren()).isEmpty(); - assertThat(tagNode.asString()).isEqualTo("TAG"); - - // KeyDerivation under Mac - INode keyDerivationNode = macNode.getChildren().get(KeyDerivation.class); - assertThat(keyDerivationNode).isNotNull(); - assertThat(keyDerivationNode.getChildren()).isEmpty(); - assertThat(keyDerivationNode.asString()).isEqualTo("KEYDERIVATION"); - - // BlockCipher under Mac - INode blockCipherNode = macNode.getChildren().get(BlockCipher.class); - assertThat(blockCipherNode).isNotNull(); - assertThat(blockCipherNode.getChildren()).hasSize(3); - assertThat(blockCipherNode.asString()).isEqualTo("AES-CTR"); - - // BlockSize under BlockCipher under Mac - INode blockSizeNode = blockCipherNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("128"); - - // Mode under BlockCipher under Mac - INode modeNode = blockCipherNode.getChildren().get(Mode.class); - assertThat(modeNode).isNotNull(); - assertThat(modeNode.getChildren()).isEmpty(); - assertThat(modeNode.asString()).isEqualTo("CTR"); - - // Oid under BlockCipher under Mac - INode oidNode = blockCipherNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.1"); - - // KeyLength under Mac - INode keyLengthNode = macNode.getChildren().get(KeyLength.class); - assertThat(keyLengthNode).isNotNull(); - assertThat(keyLengthNode.getChildren()).isEmpty(); - assertThat(keyLengthNode.asString()).isEqualTo("256"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaKBKDFHMACTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaKBKDFHMACTest.java deleted file mode 100644 index ef0fad6b9..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaKBKDFHMACTest.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.kdf; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.Algorithm; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeySize; -import com.ibm.engine.model.context.KeyDerivationFunctionContext; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.DigestSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.KeyLength; -import com.ibm.mapper.model.Mac; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.Mode; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.mapper.model.functionality.KeyDerivation; -import com.ibm.mapper.model.functionality.Tag; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaKBKDFHMACTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/kdf/PycaKBKDFHMACTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(Algorithm.class); - assertThat(value0.asString()).isEqualTo("SHA256"); - - DetectionStore store_1 = - getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); - assertThat(store_1.getDetectionValues()).hasSize(1); - assertThat(store_1.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0_1 = store_1.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(com.ibm.engine.model.Mode.class); - assertThat(value0_1.asString()).isEqualTo("CounterMode"); - - DetectionStore store_2 = - getStoreOfValueType(KeySize.class, detectionStore.getChildren()); - assertThat(store_2.getDetectionValues()).hasSize(1); - assertThat(store_2.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0_2 = store_2.getDetectionValues().get(0); - assertThat(value0_2).isInstanceOf(KeySize.class); - assertThat(value0_2.asString()).isEqualTo("256"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // Mac - INode macNode = nodes.get(0); - assertThat(macNode.getKind()).isEqualTo(Mac.class); - assertThat(macNode.getChildren()).hasSize(5); - assertThat(macNode.asString()).isEqualTo("HMAC-SHA-256"); - - // Tag under Mac - INode tagNode = macNode.getChildren().get(Tag.class); - assertThat(tagNode).isNotNull(); - assertThat(tagNode.getChildren()).isEmpty(); - assertThat(tagNode.asString()).isEqualTo("TAG"); - - // Oid under Mac - INode oidNode = macNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("1.2.840.113549.2.9"); - - // KeyLength under Mac - INode keyLengthNode = macNode.getChildren().get(KeyLength.class); - assertThat(keyLengthNode).isNotNull(); - assertThat(keyLengthNode.getChildren()).isEmpty(); - assertThat(keyLengthNode.asString()).isEqualTo("256"); - - // KeyDerivation under Mac - INode keyDerivationNode = macNode.getChildren().get(KeyDerivation.class); - assertThat(keyDerivationNode).isNotNull(); - assertThat(keyDerivationNode.getChildren()).isEmpty(); - assertThat(keyDerivationNode.asString()).isEqualTo("KEYDERIVATION"); - - // MessageDigest under Mac - INode messageDigestNode = macNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(5); - assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); - - // Mode under MessageDigest under Mac - INode modeNode = messageDigestNode.getChildren().get(Mode.class); - assertThat(modeNode).isNotNull(); - assertThat(modeNode.getChildren()).isEmpty(); - assertThat(modeNode.asString()).isEqualTo("CTR"); - - // DigestSize under MessageDigest under Mac - INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); - assertThat(digestSizeNode).isNotNull(); - assertThat(digestSizeNode.getChildren()).isEmpty(); - assertThat(digestSizeNode.asString()).isEqualTo("256"); - - // Oid under MessageDigest under Mac - INode oidNode1 = messageDigestNode.getChildren().get(Oid.class); - assertThat(oidNode1).isNotNull(); - assertThat(oidNode1.getChildren()).isEmpty(); - assertThat(oidNode1.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); - - // Digest under MessageDigest under Mac - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - - // BlockSize under MessageDigest under Mac - INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("512"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaPBKDF2Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaPBKDF2Test.java deleted file mode 100644 index bdcc9af1f..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaPBKDF2Test.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.kdf; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.Algorithm; -import com.ibm.engine.model.AlgorithmParameter; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeySize; -import com.ibm.engine.model.context.KeyDerivationFunctionContext; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.DigestSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.KeyLength; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.PasswordBasedKeyDerivationFunction; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.mapper.model.functionality.KeyDerivation; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaPBKDF2Test extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/kdf/PycaPBKDF2TestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(Algorithm.class); - assertThat(value0.asString()).isEqualTo("SHA256"); - - DetectionStore store_1 = - getStoreOfValueType(KeySize.class, detectionStore.getChildren()); - assertThat(store_1.getDetectionValues()).hasSize(1); - assertThat(store_1.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0_1 = store_1.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(KeySize.class); - assertThat(value0_1.asString()).isEqualTo("256"); - - DetectionStore store_2 = - getStoreOfValueType(AlgorithmParameter.class, detectionStore.getChildren()); - assertThat(store_2.getDetectionValues()).hasSize(1); - assertThat(store_2.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0_2 = store_2.getDetectionValues().get(0); - assertThat(value0_2).isInstanceOf(AlgorithmParameter.class); - assertThat(value0_2.asString()).isEqualTo("480000"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // PasswordBasedKeyDerivationFunction - INode passwordBasedKeyDerivationFunctionNode = nodes.get(0); - assertThat(passwordBasedKeyDerivationFunctionNode.getKind()) - .isEqualTo(PasswordBasedKeyDerivationFunction.class); - assertThat(passwordBasedKeyDerivationFunctionNode.getChildren()).hasSize(3); - assertThat(passwordBasedKeyDerivationFunctionNode.asString()).isEqualTo("PBKDF2-SHA-256"); - - // KeyDerivation under PasswordBasedKeyDerivationFunction - INode keyDerivationNode = - passwordBasedKeyDerivationFunctionNode.getChildren().get(KeyDerivation.class); - assertThat(keyDerivationNode).isNotNull(); - assertThat(keyDerivationNode.getChildren()).isEmpty(); - assertThat(keyDerivationNode.asString()).isEqualTo("KEYDERIVATION"); - - // MessageDigest under PasswordBasedKeyDerivationFunction - INode messageDigestNode = - passwordBasedKeyDerivationFunctionNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(4); - assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); - - // Digest under MessageDigest under PasswordBasedKeyDerivationFunction - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - - // BlockSize under MessageDigest under PasswordBasedKeyDerivationFunction - INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("512"); - - // DigestSize under MessageDigest under PasswordBasedKeyDerivationFunction - INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); - assertThat(digestSizeNode).isNotNull(); - assertThat(digestSizeNode.getChildren()).isEmpty(); - assertThat(digestSizeNode.asString()).isEqualTo("256"); - - // Oid under MessageDigest under PasswordBasedKeyDerivationFunction - INode oidNode = messageDigestNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); - - // KeyLength under PasswordBasedKeyDerivationFunction - INode keyLengthNode = - passwordBasedKeyDerivationFunctionNode.getChildren().get(KeyLength.class); - assertThat(keyLengthNode).isNotNull(); - assertThat(keyLengthNode.getChildren()).isEmpty(); - assertThat(keyLengthNode.asString()).isEqualTo("256"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaScryptTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaScryptTest.java deleted file mode 100644 index b137aabb5..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaScryptTest.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.kdf; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeySize; -import com.ibm.engine.model.ValueAction; -import com.ibm.engine.model.context.KeyDerivationFunctionContext; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.KeyLength; -import com.ibm.mapper.model.PasswordBasedKeyDerivationFunction; -import com.ibm.mapper.model.functionality.KeyDerivation; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaScryptTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/kdf/PycaScryptTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(ValueAction.class); - assertThat(value0.asString()).isEqualTo("Scrypt"); - - DetectionStore store_1 = - getStoreOfValueType(KeySize.class, detectionStore.getChildren()); - assertThat(store_1.getDetectionValues()).hasSize(1); - assertThat(store_1.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0_1 = store_1.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(KeySize.class); - assertThat(value0_1.asString()).isEqualTo("256"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // PasswordBasedKeyDerivationFunction - INode passwordBasedKeyDerivationFunctionNode = nodes.get(0); - assertThat(passwordBasedKeyDerivationFunctionNode.getKind()) - .isEqualTo(PasswordBasedKeyDerivationFunction.class); - assertThat(passwordBasedKeyDerivationFunctionNode.getChildren()).hasSize(2); - assertThat(passwordBasedKeyDerivationFunctionNode.asString()).isEqualTo("scrypt"); - - // KeyDerivation under PasswordBasedKeyDerivationFunction - INode keyDerivationNode = - passwordBasedKeyDerivationFunctionNode.getChildren().get(KeyDerivation.class); - assertThat(keyDerivationNode).isNotNull(); - assertThat(keyDerivationNode.getChildren()).isEmpty(); - assertThat(keyDerivationNode.asString()).isEqualTo("KEYDERIVATION"); - - // KeyLength under PasswordBasedKeyDerivationFunction - INode keyLengthNode = - passwordBasedKeyDerivationFunctionNode.getChildren().get(KeyLength.class); - assertThat(keyLengthNode).isNotNull(); - assertThat(keyLengthNode.getChildren()).isEmpty(); - assertThat(keyLengthNode.asString()).isEqualTo("256"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaX963KDFTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaX963KDFTest.java deleted file mode 100644 index 786fa46cb..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaX963KDFTest.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.kdf; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.Algorithm; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeySize; -import com.ibm.engine.model.context.KeyDerivationFunctionContext; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.DigestSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.KeyDerivationFunction; -import com.ibm.mapper.model.KeyLength; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.mapper.model.functionality.KeyDerivation; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaX963KDFTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/kdf/PycaX963KDFTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(Algorithm.class); - assertThat(value0.asString()).isEqualTo("SHA256"); - - DetectionStore store_1 = - getStoreOfValueType(KeySize.class, detectionStore.getChildren()); - assertThat(store_1.getDetectionValues()).hasSize(1); - assertThat(store_1.getDetectionValueContext()) - .isInstanceOf(KeyDerivationFunctionContext.class); - IValue value0_1 = store_1.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(KeySize.class); - assertThat(value0_1.asString()).isEqualTo("256"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // KeyDerivationFunction - INode keyDerivationFunctionNode = nodes.get(0); - assertThat(keyDerivationFunctionNode.getKind()).isEqualTo(KeyDerivationFunction.class); - assertThat(keyDerivationFunctionNode.getChildren()).hasSize(3); - assertThat(keyDerivationFunctionNode.asString()).isEqualTo("ANSI-KDF-X9.63"); - - // KeyDerivation under KeyDerivationFunction - INode keyDerivationNode = keyDerivationFunctionNode.getChildren().get(KeyDerivation.class); - assertThat(keyDerivationNode).isNotNull(); - assertThat(keyDerivationNode.getChildren()).isEmpty(); - assertThat(keyDerivationNode.asString()).isEqualTo("KEYDERIVATION"); - - // KeyLength under KeyDerivationFunction - INode keyLengthNode = keyDerivationFunctionNode.getChildren().get(KeyLength.class); - assertThat(keyLengthNode).isNotNull(); - assertThat(keyLengthNode.getChildren()).isEmpty(); - assertThat(keyLengthNode.asString()).isEqualTo("256"); - - // MessageDigest under KeyDerivationFunction - INode messageDigestNode = keyDerivationFunctionNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(4); - assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); - - // BlockSize under MessageDigest under KeyDerivationFunction - INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("512"); - - // Oid under MessageDigest under KeyDerivationFunction - INode oidNode = messageDigestNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); - - // DigestSize under MessageDigest under KeyDerivationFunction - INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); - assertThat(digestSizeNode).isNotNull(); - assertThat(digestSizeNode.getChildren()).isEmpty(); - assertThat(digestSizeNode.asString()).isEqualTo("256"); - - // Digest under MessageDigest under KeyDerivationFunction - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/keyagreement/PycaKeyAgreementTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/keyagreement/PycaKeyAgreementTest.java deleted file mode 100644 index bb88f0e77..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/keyagreement/PycaKeyAgreementTest.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.keyagreement; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.KeyAction; -import com.ibm.engine.model.context.KeyAgreementContext; -import com.ibm.mapper.model.EllipticCurve; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.KeyAgreement; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.functionality.KeyGeneration; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaKeyAgreementTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/keyagreement/PycaKeyAgreementTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - - if (findingId == 0) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()) - .isInstanceOf(KeyAgreementContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(KeyAction.class); - assertThat(value0.asString()).isEqualTo("GENERATION"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // KeyAgreement - INode keyAgreementNode = nodes.get(0); - assertThat(keyAgreementNode.getKind()).isEqualTo(KeyAgreement.class); - assertThat(keyAgreementNode.getChildren()).hasSize(3); - assertThat(keyAgreementNode.asString()).isEqualTo("x25519"); - - // EllipticCurve under KeyAgreement - INode ellipticCurveNode = keyAgreementNode.getChildren().get(EllipticCurve.class); - assertThat(ellipticCurveNode).isNotNull(); - assertThat(ellipticCurveNode.getChildren()).isEmpty(); - assertThat(ellipticCurveNode.asString()).isEqualTo("Curve25519"); - - // KeyGeneration under KeyAgreement - INode keyGenerationNode = keyAgreementNode.getChildren().get(KeyGeneration.class); - assertThat(keyGenerationNode).isNotNull(); - assertThat(keyGenerationNode.getChildren()).isEmpty(); - assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); - - // Oid under KeyAgreement - INode oidNode1 = keyAgreementNode.getChildren().get(Oid.class); - assertThat(oidNode1).isNotNull(); - assertThat(oidNode1.getChildren()).isEmpty(); - assertThat(oidNode1.asString()).isEqualTo("1.3.101.110"); - - } else if (findingId == 1) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()) - .isInstanceOf(KeyAgreementContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(KeyAction.class); - assertThat(value0.asString()).isEqualTo("GENERATION"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // KeyAgreement - INode keyAgreementNode = nodes.get(0); - assertThat(keyAgreementNode.getKind()).isEqualTo(KeyAgreement.class); - assertThat(keyAgreementNode.getChildren()).hasSize(3); - assertThat(keyAgreementNode.asString()).isEqualTo("x25519"); - - // EllipticCurve under KeyAgreement - INode ellipticCurveNode = keyAgreementNode.getChildren().get(EllipticCurve.class); - assertThat(ellipticCurveNode).isNotNull(); - assertThat(ellipticCurveNode.getChildren()).isEmpty(); - assertThat(ellipticCurveNode.asString()).isEqualTo("Curve25519"); - - // KeyGeneration under KeyAgreement - INode keyGenerationNode = keyAgreementNode.getChildren().get(KeyGeneration.class); - assertThat(keyGenerationNode).isNotNull(); - assertThat(keyGenerationNode.getChildren()).isEmpty(); - assertThat(keyGenerationNode.asString()).isEqualTo("KEYGENERATION"); - - // Oid under KeyAgreement - INode oidNode1 = keyAgreementNode.getChildren().get(Oid.class); - assertThat(oidNode1).isNotNull(); - assertThat(oidNode1.getChildren()).isEmpty(); - assertThat(oidNode1.asString()).isEqualTo("1.3.101.110"); - - } else { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()) - .isInstanceOf(KeyAgreementContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(KeyAction.class); - assertThat(value0.asString()).isEqualTo("GENERATION"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // KeyAgreement - INode keyAgreementNode1 = nodes.get(0); - assertThat(keyAgreementNode1.getKind()).isEqualTo(KeyAgreement.class); - assertThat(keyAgreementNode1.getChildren()).hasSize(3); - assertThat(keyAgreementNode1.asString()).isEqualTo("x448"); - - // EllipticCurve under KeyAgreement - INode ellipticCurveNode1 = keyAgreementNode1.getChildren().get(EllipticCurve.class); - assertThat(ellipticCurveNode1).isNotNull(); - assertThat(ellipticCurveNode1.getChildren()).isEmpty(); - assertThat(ellipticCurveNode1.asString()).isEqualTo("Curve448"); - - // KeyGeneration under KeyAgreement - INode keyGenerationNode1 = keyAgreementNode1.getChildren().get(KeyGeneration.class); - assertThat(keyGenerationNode1).isNotNull(); - assertThat(keyGenerationNode1.getChildren()).isEmpty(); - assertThat(keyGenerationNode1.asString()).isEqualTo("KEYGENERATION"); - - // Oid under KeyAgreement - INode oidNode3 = keyAgreementNode1.getChildren().get(Oid.class); - assertThat(oidNode3).isNotNull(); - assertThat(oidNode3.getChildren()).isEmpty(); - assertThat(oidNode3.asString()).isEqualTo("1.3.101.111"); - } - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaCMACTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaCMACTest.java deleted file mode 100644 index bbc0f3f2b..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaCMACTest.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.mac; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.Algorithm; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.context.MacContext; -import com.ibm.mapper.model.BlockCipher; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.Mac; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.functionality.Tag; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaCMACTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify("src/test/files/rules/detection/mac/PycaCMACTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(MacContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(Algorithm.class); - assertThat(value0.asString()).isEqualTo("AES"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // Mac - INode macNode = nodes.get(0); - assertThat(macNode.getKind()).isEqualTo(Mac.class); - assertThat(macNode.getChildren()).hasSize(2); - assertThat(macNode.asString()).isEqualTo("CMAC-AES"); - - // BlockCipher under Mac - INode blockCipherNode = macNode.getChildren().get(BlockCipher.class); - assertThat(blockCipherNode).isNotNull(); - assertThat(blockCipherNode.getChildren()).hasSize(2); - assertThat(blockCipherNode.asString()).isEqualTo("AES"); - - // BlockSize under BlockCipher under Mac - INode blockSizeNode = blockCipherNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("128"); - - // Oid under BlockCipher under Mac - INode oidNode = blockCipherNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.1"); - - // Tag under Mac - INode tagNode = macNode.getChildren().get(Tag.class); - assertThat(tagNode).isNotNull(); - assertThat(tagNode.getChildren()).isEmpty(); - assertThat(tagNode.asString()).isEqualTo("TAG"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaHMACTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaHMACTest.java deleted file mode 100644 index 9957f5381..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaHMACTest.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.mac; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.Algorithm; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.context.MacContext; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.DigestSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.Mac; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.mapper.model.functionality.Tag; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaHMACTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify("src/test/files/rules/detection/mac/PycaHMACTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(MacContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(Algorithm.class); - assertThat(value0.asString()).isEqualTo("SHA256"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // Mac - INode macNode = nodes.get(0); - assertThat(macNode.getKind()).isEqualTo(Mac.class); - assertThat(macNode.getChildren()).hasSize(3); - assertThat(macNode.asString()).isEqualTo("HMAC-SHA-256"); - - // MessageDigest under Mac - INode messageDigestNode = macNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(4); - assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); - - // Digest under MessageDigest under Mac - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - - // BlockSize under MessageDigest under Mac - INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("512"); - - // Oid under MessageDigest under Mac - INode oidNode = messageDigestNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); - - // DigestSize under MessageDigest under Mac - INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); - assertThat(digestSizeNode).isNotNull(); - assertThat(digestSizeNode.getChildren()).isEmpty(); - assertThat(digestSizeNode.asString()).isEqualTo("256"); - - // Tag under Mac - INode tagNode = macNode.getChildren().get(Tag.class); - assertThat(tagNode).isNotNull(); - assertThat(tagNode.getChildren()).isEmpty(); - assertThat(tagNode.asString()).isEqualTo("TAG"); - - // Oid under Mac - INode oidNode1 = macNode.getChildren().get(Oid.class); - assertThat(oidNode1).isNotNull(); - assertThat(oidNode1.getChildren()).isEmpty(); - assertThat(oidNode1.asString()).isEqualTo("1.2.840.113549.2.9"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaMacDetectionInCustomFunctionTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaMacDetectionInCustomFunctionTest.java deleted file mode 100644 index 9a89f28eb..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaMacDetectionInCustomFunctionTest.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.mac; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.Algorithm; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.context.MacContext; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.DigestSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.Mac; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.mapper.model.functionality.Tag; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -/** - * Verifies that cryptographic operations inside user-defined functions are detected during standard - * AST traversal. This test covers both positive cases (cryptographic operations) and negative cases - * (non-cryptographic functions). - */ -class PycaMacDetectionInCustomFunctionTest extends TestBase { - - @Test - void testCryptographicOperationInCustomFunction() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/mac/PycaMacDetectionInCustomFunctionTestFile.py", - this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - - // Verifies that cryptographic operations inside user-defined functions are detected - // during standard AST traversal. - - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(MacContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(Algorithm.class); - assertThat(value0.asString()).isEqualTo("SHA256"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // Mac - INode macNode = nodes.get(0); - assertThat(macNode.getKind()).isEqualTo(Mac.class); - assertThat(macNode.getChildren()).hasSize(3); - assertThat(macNode.asString()).isEqualTo("HMAC-SHA-256"); - - // MessageDigest under Mac - INode messageDigestNode = macNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(4); - assertThat(messageDigestNode.asString()).isEqualTo("SHA-256"); - - // Digest under MessageDigest under Mac - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - - // BlockSize under MessageDigest under Mac - INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("512"); - - // Oid under MessageDigest under Mac - INode oidNode = messageDigestNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); - - // DigestSize under MessageDigest under Mac - INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); - assertThat(digestSizeNode).isNotNull(); - assertThat(digestSizeNode.getChildren()).isEmpty(); - assertThat(digestSizeNode.asString()).isEqualTo("256"); - - // Tag under Mac - INode tagNode = macNode.getChildren().get(Tag.class); - assertThat(tagNode).isNotNull(); - assertThat(tagNode.getChildren()).isEmpty(); - assertThat(tagNode.asString()).isEqualTo("TAG"); - - // Oid under Mac - INode oidNode1 = macNode.getChildren().get(Oid.class); - assertThat(oidNode1).isNotNull(); - assertThat(oidNode1.getChildren()).isEmpty(); - assertThat(oidNode1.asString()).isEqualTo("1.2.840.113549.2.9"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaPoly1305Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaPoly1305Test.java deleted file mode 100644 index fecc5bd37..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaPoly1305Test.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.mac; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.ValueAction; -import com.ibm.engine.model.context.MacContext; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.Mac; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.functionality.Digest; -import com.ibm.mapper.model.functionality.Tag; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaPoly1305Test extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/mac/PycaPoly1305TestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(MacContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(ValueAction.class); - assertThat(value0.asString()).isEqualTo("Poly1305"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // Mac - INode macNode = nodes.get(0); - assertThat(macNode.getKind()).isEqualTo(Mac.class); - assertThat(macNode.getChildren()).hasSize(2); - assertThat(macNode.asString()).isEqualTo("HMAC-Poly1305"); - - // Tag under Mac - INode tagNode = macNode.getChildren().get(Tag.class); - assertThat(tagNode).isNotNull(); - assertThat(tagNode.getChildren()).isEmpty(); - assertThat(tagNode.asString()).isEqualTo("TAG"); - - // MessageDigest under Mac - INode messageDigestNode = macNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(1); - assertThat(messageDigestNode.asString()).isEqualTo("Poly1305"); - - // Digest under MessageDigest under Mac - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/padding/PycaPaddingTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/padding/PycaPaddingTest.java deleted file mode 100644 index a2f29030a..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/padding/PycaPaddingTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.padding; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.Algorithm; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.ValueAction; -import com.ibm.engine.model.context.CipherContext; -import com.ibm.mapper.model.BlockCipher; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.Mode; -import com.ibm.mapper.model.Padding; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaPaddingTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/padding/PycaPaddingTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(Algorithm.class); - assertThat(value0.asString()).isEqualTo("CAST5"); - - DetectionStore store_1 = - getStoreOfValueType(ValueAction.class, detectionStore.getChildren()); - assertThat(store_1.getDetectionValues()).hasSize(1); - assertThat(store_1.getDetectionValueContext()).isInstanceOf(CipherContext.class); - IValue value0_1 = store_1.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(ValueAction.class); - assertThat(value0_1.asString()).isEqualTo("ANSIX923"); - - DetectionStore store_1_1 = - getStoreOfValueType(com.ibm.engine.model.BlockSize.class, store_1.getChildren()); - assertThat(store_1_1.getDetectionValues()).hasSize(1); - assertThat(store_1_1.getDetectionValueContext()).isInstanceOf(CipherContext.class); - IValue value0_1_1 = store_1_1.getDetectionValues().get(0); - assertThat(value0_1_1).isInstanceOf(com.ibm.engine.model.BlockSize.class); - assertThat(value0_1_1.asString()).isEqualTo("128"); - - DetectionStore store_2 = - getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); - assertThat(store_2.getDetectionValues()).hasSize(1); - assertThat(store_2.getDetectionValueContext()).isInstanceOf(CipherContext.class); - IValue value0_2 = store_2.getDetectionValues().get(0); - assertThat(value0_2).isInstanceOf(com.ibm.engine.model.Mode.class); - assertThat(value0_2.asString()).isEqualTo("CFB"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // BlockCipher - INode blockCipherNode = nodes.get(0); - assertThat(blockCipherNode.getKind()).isEqualTo(BlockCipher.class); - assertThat(blockCipherNode.getChildren()).hasSize(3); - assertThat(blockCipherNode.asString()).isEqualTo("CAST5-CFB"); - - // BlockSize under BlockCipher - INode blockSizeNode = blockCipherNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("64"); - - // Mode under BlockCipher - INode modeNode = blockCipherNode.getChildren().get(Mode.class); - assertThat(modeNode).isNotNull(); - assertThat(modeNode.getChildren()).isEmpty(); - assertThat(modeNode.asString()).isEqualTo("CFB"); - - // Padding under BlockCipher - INode paddingNode = blockCipherNode.getChildren().get(Padding.class); - assertThat(paddingNode).isNotNull(); - assertThat(paddingNode.getChildren()).hasSize(1); - assertThat(paddingNode.asString()).isEqualTo("ANSI X9.23"); - - // BlockSize under Padding under BlockCipher - INode blockSizeNode1 = paddingNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode1).isNotNull(); - assertThat(blockSizeNode1.getChildren()).isEmpty(); - assertThat(blockSizeNode1.asString()).isEqualTo("128"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/symmetric/PycaCipher1Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/symmetric/PycaCipher1Test.java deleted file mode 100644 index 73a1d6ebb..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/symmetric/PycaCipher1Test.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.symmetric; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.Algorithm; -import com.ibm.engine.model.CipherAction; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.ValueAction; -import com.ibm.engine.model.context.CipherContext; -import com.ibm.mapper.model.BlockCipher; -import com.ibm.mapper.model.BlockSize; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.Mode; -import com.ibm.mapper.model.Oid; -import com.ibm.mapper.model.Padding; -import com.ibm.mapper.model.functionality.Decrypt; -import com.ibm.mapper.model.functionality.Encrypt; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaCipher1Test extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/symmetric/PycaCipher1TestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(Algorithm.class); - assertThat(value0.asString()).isEqualTo("AES"); - - List> store_1 = - getStoresOfValueType(CipherAction.class, detectionStore.getChildren()); - for (DetectionStore store : store_1) { - assertThat(store.getDetectionValues()).hasSize(1); - assertThat(store.getDetectionValueContext()).isInstanceOf(CipherContext.class); - IValue value0_1 = store.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(CipherAction.class); - assertThat(value0_1.asString()) - .satisfiesAnyOf( - s -> assertThat(s).isEqualTo("DECRYPT"), - s -> assertThat(s).isEqualTo("ENCRYPT")); - } - - DetectionStore store_3 = - getStoreOfValueType(ValueAction.class, detectionStore.getChildren()); - assertThat(store_3.getDetectionValues()).hasSize(1); - assertThat(store_3.getDetectionValueContext()).isInstanceOf(CipherContext.class); - IValue value0_3 = store_3.getDetectionValues().get(0); - assertThat(value0_3).isInstanceOf(ValueAction.class); - assertThat(value0_3.asString()).isEqualTo("PKCS7"); - - DetectionStore store_3_1 = - getStoreOfValueType(com.ibm.engine.model.BlockSize.class, store_3.getChildren()); - assertThat(store_3_1.getDetectionValues()).hasSize(1); - assertThat(store_3_1.getDetectionValueContext()).isInstanceOf(CipherContext.class); - IValue value0_3_1 = store_3_1.getDetectionValues().get(0); - assertThat(value0_3_1).isInstanceOf(com.ibm.engine.model.BlockSize.class); - assertThat(value0_3_1.asString()).isEqualTo("80"); - - DetectionStore store_4 = - getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); - assertThat(store_4.getDetectionValues()).hasSize(1); - assertThat(store_4.getDetectionValueContext()).isInstanceOf(CipherContext.class); - IValue value0_4 = store_4.getDetectionValues().get(0); - assertThat(value0_4).isInstanceOf(com.ibm.engine.model.Mode.class); - assertThat(value0_4.asString()).isEqualTo("CBC"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // BlockCipher - INode blockCipherNode = nodes.get(0); - assertThat(blockCipherNode.getKind()).isEqualTo(BlockCipher.class); - assertThat(blockCipherNode.getChildren()).hasSize(6); - assertThat(blockCipherNode.asString()).isEqualTo("AES-CBC-PKCS7"); - - // Mode under BlockCipher - INode modeNode = blockCipherNode.getChildren().get(Mode.class); - assertThat(modeNode).isNotNull(); - assertThat(modeNode.getChildren()).isEmpty(); - assertThat(modeNode.asString()).isEqualTo("CBC"); - - // Decrypt under BlockCipher - INode decryptNode = blockCipherNode.getChildren().get(Decrypt.class); - assertThat(decryptNode).isNotNull(); - assertThat(decryptNode.getChildren()).isEmpty(); - assertThat(decryptNode.asString()).isEqualTo("DECRYPT"); - - // Oid under BlockCipher - INode oidNode = blockCipherNode.getChildren().get(Oid.class); - assertThat(oidNode).isNotNull(); - assertThat(oidNode.getChildren()).isEmpty(); - assertThat(oidNode.asString()).isEqualTo("2.16.840.1.101.3.4.1"); - - // Encrypt under BlockCipher - INode encryptNode = blockCipherNode.getChildren().get(Encrypt.class); - assertThat(encryptNode).isNotNull(); - assertThat(encryptNode.getChildren()).isEmpty(); - assertThat(encryptNode.asString()).isEqualTo("ENCRYPT"); - - // Padding under BlockCipher - INode paddingNode = blockCipherNode.getChildren().get(Padding.class); - assertThat(paddingNode).isNotNull(); - assertThat(paddingNode.getChildren()).hasSize(1); - assertThat(paddingNode.asString()).isEqualTo("PKCS7"); - - // BlockSize under Padding under BlockCipher - INode blockSizeNode = paddingNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode).isNotNull(); - assertThat(blockSizeNode.getChildren()).isEmpty(); - assertThat(blockSizeNode.asString()).isEqualTo("80"); - - // BlockSize under BlockCipher - INode blockSizeNode1 = blockCipherNode.getChildren().get(BlockSize.class); - assertThat(blockSizeNode1).isNotNull(); - assertThat(blockSizeNode1.getChildren()).isEmpty(); - assertThat(blockSizeNode1.asString()).isEqualTo("128"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/symmetric/PycaCipher2Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/symmetric/PycaCipher2Test.java deleted file mode 100644 index 74ac657a9..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/symmetric/PycaCipher2Test.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.symmetric; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.Algorithm; -import com.ibm.engine.model.CipherAction; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.context.CipherContext; -import com.ibm.mapper.model.BlockCipher; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.Mode; -import com.ibm.mapper.model.functionality.Encrypt; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaCipher2Test extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/symmetric/PycaCipher2TestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(Algorithm.class); - assertThat(value0.asString()).isEqualTo("Camellia"); - - DetectionStore store_1 = - getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); - assertThat(store_1.getDetectionValues()).hasSize(1); - assertThat(store_1.getDetectionValueContext()).isInstanceOf(CipherContext.class); - IValue value0_1 = store_1.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(CipherAction.class); - assertThat(value0_1.asString()).isEqualTo("ENCRYPT"); - - DetectionStore store_2 = - getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); - assertThat(store_2.getDetectionValues()).hasSize(1); - assertThat(store_2.getDetectionValueContext()).isInstanceOf(CipherContext.class); - IValue value0_2 = store_2.getDetectionValues().get(0); - assertThat(value0_2).isInstanceOf(com.ibm.engine.model.Mode.class); - assertThat(value0_2.asString()).isEqualTo("OFB"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // BlockCipher - INode blockCipherNode = nodes.get(0); - assertThat(blockCipherNode.getKind()).isEqualTo(BlockCipher.class); - assertThat(blockCipherNode.getChildren()).hasSize(2); - assertThat(blockCipherNode.asString()).isEqualTo("CAMELLIA-OFB"); - - // Encrypt under BlockCipher - INode encryptNode = blockCipherNode.getChildren().get(Encrypt.class); - assertThat(encryptNode).isNotNull(); - assertThat(encryptNode.getChildren()).isEmpty(); - assertThat(encryptNode.asString()).isEqualTo("ENCRYPT"); - - // Mode under BlockCipher - INode modeNode = blockCipherNode.getChildren().get(Mode.class); - assertThat(modeNode).isNotNull(); - assertThat(modeNode.getChildren()).isEmpty(); - assertThat(modeNode.asString()).isEqualTo("OFB"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/symmetric/PycaStreamCipher1Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/symmetric/PycaStreamCipher1Test.java deleted file mode 100644 index 7c5b1f339..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/symmetric/PycaStreamCipher1Test.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.symmetric; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.Algorithm; -import com.ibm.engine.model.CipherAction; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.context.CipherContext; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.StreamCipher; -import com.ibm.mapper.model.functionality.Encrypt; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaStreamCipher1Test extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/symmetric/PycaStreamCipher1TestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(Algorithm.class); - assertThat(value0.asString()).isEqualTo("ChaCha20"); - - DetectionStore store_1 = - getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); - assertThat(store_1.getDetectionValues()).hasSize(1); - assertThat(store_1.getDetectionValueContext()).isInstanceOf(CipherContext.class); - IValue value0_1 = store_1.getDetectionValues().get(0); - assertThat(value0_1).isInstanceOf(CipherAction.class); - assertThat(value0_1.asString()).isEqualTo("ENCRYPT"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // StreamCipher - INode streamCipherNode = nodes.get(0); - assertThat(streamCipherNode.getKind()).isEqualTo(StreamCipher.class); - assertThat(streamCipherNode.getChildren()).hasSize(1); - assertThat(streamCipherNode.asString()).isEqualTo("ChaCha20"); - - // Encrypt under BlockCipher - INode encryptNode = streamCipherNode.getChildren().get(Encrypt.class); - assertThat(encryptNode).isNotNull(); - assertThat(encryptNode.getChildren()).isEmpty(); - assertThat(encryptNode.asString()).isEqualTo("ENCRYPT"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/wrapping/PycaWrappingTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/wrapping/PycaWrappingTest.java deleted file mode 100644 index 003eaaf9c..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/wrapping/PycaWrappingTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.wrapping; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.CipherAction; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.context.CipherContext; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.KeyLength; -import com.ibm.mapper.model.KeyWrap; -import com.ibm.mapper.model.functionality.Encapsulate; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaWrappingTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/wrapping/PycaWrappingTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(CipherAction.class); - assertThat(value0.asString()).isEqualTo("WRAP"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // BlockCipher - INode blockCipherNode = nodes.get(0); - assertThat(blockCipherNode.getKind()).isEqualTo(KeyWrap.class); - assertThat(blockCipherNode.getChildren()).hasSize(4); - assertThat(blockCipherNode.asString()).isEqualTo("AES-128"); - - // KeyLength under BlockCipher - INode keyLengthNode = blockCipherNode.getChildren().get(KeyLength.class); - assertThat(keyLengthNode).isNotNull(); - assertThat(keyLengthNode.getChildren()).isEmpty(); - assertThat(keyLengthNode.asString()).isEqualTo("128"); - - // Encapsulate under BlockCipher - INode encapsulateNode = blockCipherNode.getChildren().get(Encapsulate.class); - assertThat(encapsulateNode).isNotNull(); - assertThat(encapsulateNode.getChildren()).isEmpty(); - assertThat(encapsulateNode.asString()).isEqualTo("ENCAPSULATE"); - } -} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/wrapping/PycaWrappingWithPaddingTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/wrapping/PycaWrappingWithPaddingTest.java deleted file mode 100644 index 37d0bb788..000000000 --- a/python/src/test/java/com/ibm/plugin/rules/detection/wrapping/PycaWrappingWithPaddingTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ibm.plugin.rules.detection.wrapping; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ibm.engine.detection.DetectionStore; -import com.ibm.engine.model.CipherAction; -import com.ibm.engine.model.IValue; -import com.ibm.engine.model.context.CipherContext; -import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.KeyLength; -import com.ibm.mapper.model.KeyWrap; -import com.ibm.mapper.model.functionality.Encapsulate; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.sonar.plugins.python.api.PythonCheck; -import org.sonar.plugins.python.api.PythonVisitorContext; -import org.sonar.plugins.python.api.symbols.Symbol; -import org.sonar.plugins.python.api.tree.Tree; -import org.sonar.python.checks.utils.PythonCheckVerifier; - -class PycaWrappingWithPaddingTest extends TestBase { - - @Test - void test() { - PythonCheckVerifier.verify( - "src/test/files/rules/detection/wrapping/PycaWrappingWithPaddingTestFile.py", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull DetectionStore detectionStore, - @Nonnull List nodes) { - /* - * Detection Store - */ - assertThat(detectionStore.getDetectionValues()).hasSize(1); - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(CipherAction.class); - assertThat(value0.asString()).isEqualTo("WRAP"); - - /* - * Translation - */ - assertThat(nodes).hasSize(1); - - // BlockCipher - INode blockCipherNode = nodes.get(0); - assertThat(blockCipherNode.getKind()).isEqualTo(KeyWrap.class); - assertThat(blockCipherNode.getChildren()).hasSize(4); - assertThat(blockCipherNode.asString()).isEqualTo("AES-128"); - - // KeyLength under BlockCipher - INode keyLengthNode = blockCipherNode.getChildren().get(KeyLength.class); - assertThat(keyLengthNode).isNotNull(); - assertThat(keyLengthNode.getChildren()).isEmpty(); - assertThat(keyLengthNode.asString()).isEqualTo("128"); - - // Encapsulate under BlockCipher - INode encapsulateNode = blockCipherNode.getChildren().get(Encapsulate.class); - assertThat(encapsulateNode).isNotNull(); - assertThat(encapsulateNode.getChildren()).isEmpty(); - assertThat(encapsulateNode.asString()).isEqualTo("ENCAPSULATE"); - } -}