From 322f3118412321cdd69bd04503dbae1bc263f6e4 Mon Sep 17 00:00:00 2001 From: Sachin Kumar Date: Wed, 20 May 2026 20:45:00 +0530 Subject: [PATCH 1/2] test(java): add BouncyCastle digest edge-case coverage Signed-off-by: Sachin Kumar --- .../bc/signer/BcDigestEdgeCasesTestFile.java | 36 ++++ .../bc/signer/BcDigestEdgeCasesTest.java | 186 ++++++++++++++++++ 2 files changed, 222 insertions(+) create mode 100644 java/src/test/files/rules/detection/bc/signer/BcDigestEdgeCasesTestFile.java create mode 100644 java/src/test/java/com/ibm/plugin/rules/detection/bc/signer/BcDigestEdgeCasesTest.java diff --git a/java/src/test/files/rules/detection/bc/signer/BcDigestEdgeCasesTestFile.java b/java/src/test/files/rules/detection/bc/signer/BcDigestEdgeCasesTestFile.java new file mode 100644 index 000000000..db2bf06ff --- /dev/null +++ b/java/src/test/files/rules/detection/bc/signer/BcDigestEdgeCasesTestFile.java @@ -0,0 +1,36 @@ +/* + * 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.bc.signer; + +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.digests.WhirlpoolDigest; +import org.bouncycastle.crypto.digests.TigerDigest; +import org.bouncycastle.crypto.digests.RIPEMD160Digest; + +public class BcDigestEdgeCasesTestFile { + + public static void test() { + Digest whirlpool = new WhirlpoolDigest(); // Noncompliant {{(MessageDigest) Whirlpool}} + + Digest tiger = new TigerDigest(); // Noncompliant {{(MessageDigest) Tiger}} + + Digest ripemd = new RIPEMD160Digest(); // Noncompliant {{(MessageDigest) RIPEMD-160}} + } +} diff --git a/java/src/test/java/com/ibm/plugin/rules/detection/bc/signer/BcDigestEdgeCasesTest.java b/java/src/test/java/com/ibm/plugin/rules/detection/bc/signer/BcDigestEdgeCasesTest.java new file mode 100644 index 000000000..139611eeb --- /dev/null +++ b/java/src/test/java/com/ibm/plugin/rules/detection/bc/signer/BcDigestEdgeCasesTest.java @@ -0,0 +1,186 @@ +/* + * 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.bc.signer; + +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.NumberOfIterations; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import com.ibm.plugin.rules.detection.bc.BouncyCastleJars; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.java.checks.verifier.CheckVerifier; +import org.sonar.plugins.java.api.JavaCheck; +import org.sonar.plugins.java.api.JavaFileScannerContext; +import org.sonar.plugins.java.api.semantic.Symbol; +import org.sonar.plugins.java.api.tree.Tree; + +class BcDigestEdgeCasesTest extends TestBase { + @Test + void test() { + CheckVerifier.newVerifier() + .onFile( + "src/test/files/rules/detection/bc/signer/BcDigestEdgeCasesTestFile.java") + .withChecks(this) + .withClassPath(BouncyCastleJars.latestJar) + .verifyIssues(); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 0) { + /* + * Detection Store + */ + assertThat(detectionStore).isNotNull(); + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(ValueAction.class); + assertThat(value0.asString()).isEqualTo("WhirlpoolDigest"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // MessageDigest + INode messageDigestNode = nodes.get(0); + assertThat(messageDigestNode.getKind()).isEqualTo(MessageDigest.class); + assertThat(messageDigestNode.getChildren()).hasSize(4); + assertThat(messageDigestNode.asString()).isEqualTo("Whirlpool"); + + // Digest under MessageDigest + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // BlockSize under MessageDigest + INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("512"); + + // DigestSize under MessageDigest + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("512"); + + // NumberOfIterations under MessageDigest + INode iterationsNode = messageDigestNode.getChildren().get(NumberOfIterations.class); + assertThat(iterationsNode).isNotNull(); + assertThat(iterationsNode.getChildren()).isEmpty(); + assertThat(iterationsNode.asString()).isEqualTo("10"); + } else if (findingId == 1) { + /* + * Detection Store + */ + assertThat(detectionStore).isNotNull(); + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(ValueAction.class); + assertThat(value0.asString()).isEqualTo("TigerDigest"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // MessageDigest + INode messageDigestNode = nodes.get(0); + assertThat(messageDigestNode.getKind()).isEqualTo(MessageDigest.class); + assertThat(messageDigestNode.getChildren()).hasSize(4); + assertThat(messageDigestNode.asString()).isEqualTo("Tiger"); + + // Digest under MessageDigest + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // BlockSize under MessageDigest + INode blockSizeNode = messageDigestNode.getChildren().get(BlockSize.class); + assertThat(blockSizeNode).isNotNull(); + assertThat(blockSizeNode.getChildren()).isEmpty(); + assertThat(blockSizeNode.asString()).isEqualTo("512"); + + // DigestSize under MessageDigest + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("192"); + + // NumberOfIterations under MessageDigest + INode iterationsNode = messageDigestNode.getChildren().get(NumberOfIterations.class); + assertThat(iterationsNode).isNotNull(); + assertThat(iterationsNode.getChildren()).isEmpty(); + assertThat(iterationsNode.asString()).isEqualTo("24"); + } else if (findingId == 2) { + /* + * Detection Store + */ + assertThat(detectionStore).isNotNull(); + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(ValueAction.class); + assertThat(value0.asString()).isEqualTo("RIPEMD160Digest"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + // MessageDigest + INode messageDigestNode = nodes.get(0); + assertThat(messageDigestNode.getKind()).isEqualTo(MessageDigest.class); + assertThat(messageDigestNode.getChildren()).hasSize(2); + assertThat(messageDigestNode.asString()).isEqualTo("RIPEMD-160"); + + // Digest under MessageDigest + INode digestNode = messageDigestNode.getChildren().get(Digest.class); + assertThat(digestNode).isNotNull(); + assertThat(digestNode.getChildren()).isEmpty(); + assertThat(digestNode.asString()).isEqualTo("DIGEST"); + + // DigestSize under MessageDigest + INode digestSizeNode = messageDigestNode.getChildren().get(DigestSize.class); + assertThat(digestSizeNode).isNotNull(); + assertThat(digestSizeNode.getChildren()).isEmpty(); + assertThat(digestSizeNode.asString()).isEqualTo("160"); + } + } +} From a9d8ae8538a7a17bc2f22687f85621b0f004f97d Mon Sep 17 00:00:00 2001 From: Sachin Kumar Date: Wed, 20 May 2026 20:56:07 +0530 Subject: [PATCH 2/2] test(java): guard unexpected digest findings Signed-off-by: Sachin Kumar --- .../rules/detection/bc/signer/BcDigestEdgeCasesTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/java/src/test/java/com/ibm/plugin/rules/detection/bc/signer/BcDigestEdgeCasesTest.java b/java/src/test/java/com/ibm/plugin/rules/detection/bc/signer/BcDigestEdgeCasesTest.java index 139611eeb..eb1d30161 100644 --- a/java/src/test/java/com/ibm/plugin/rules/detection/bc/signer/BcDigestEdgeCasesTest.java +++ b/java/src/test/java/com/ibm/plugin/rules/detection/bc/signer/BcDigestEdgeCasesTest.java @@ -20,6 +20,7 @@ package com.ibm.plugin.rules.detection.bc.signer; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; import com.ibm.engine.detection.DetectionStore; import com.ibm.engine.model.IValue; @@ -181,6 +182,8 @@ public void asserts( assertThat(digestSizeNode).isNotNull(); assertThat(digestSizeNode.getChildren()).isEmpty(); assertThat(digestSizeNode.asString()).isEqualTo("160"); + } else { + fail("Unexpected findingId: " + findingId); } } }