diff --git a/.gitignore b/.gitignore index dbcf57e71..7ca76c60c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,14 @@ target/ !**/src/main/**/target/ !**/src/test/**/target/ +### Generated scan output ### +cbom.json +maven_fast/ +*.tokens +*.interp +generated-sources/ +.antlr/ + ### IntelliJ IDEA ### .idea *.iws diff --git a/cxx/pom.xml b/cxx/pom.xml new file mode 100644 index 000000000..47ba9a1a8 --- /dev/null +++ b/cxx/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + + com.ibm + sonar-cryptography + 2.0.0-SNAPSHOT + + + cxx + + + 17 + 17 + UTF-8 + + + + + com.ibm + engine + 2.0.0-SNAPSHOT + + + com.ibm + output + 2.0.0-SNAPSHOT + + + com.ibm + enricher + 2.0.0-SNAPSHOT + + + com.ibm + rules + 2.0.0-SNAPSHOT + + + com.ibm + common + 2.0.0-SNAPSHOT + + + com.ibm + mapper + 2.0.0-SNAPSHOT + + + + + + + org.sonarsource.analyzer-commons + sonar-analyzer-test-commons + 2.18.0.3393 + test + + + + diff --git a/cxx/src/main/java/com/ibm/plugin/CryptoCxxSensor.java b/cxx/src/main/java/com/ibm/plugin/CryptoCxxSensor.java new file mode 100644 index 000000000..63bce6b6b --- /dev/null +++ b/cxx/src/main/java/com/ibm/plugin/CryptoCxxSensor.java @@ -0,0 +1,133 @@ +/* + * 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; + +import com.ibm.engine.language.cxx.CxxCheck; +import com.ibm.engine.language.cxx.CxxParserErrorListener; +import com.ibm.engine.language.cxx.CxxScanContext; +import com.ibm.engine.language.cxx.antlr.CPP14Lexer; +import com.ibm.engine.language.cxx.antlr.CPP14Parser; +import java.io.IOException; +import java.util.Collection; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.rule.CheckFactory; +import org.sonar.api.batch.sensor.Sensor; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorDescriptor; + +public class CryptoCxxSensor implements Sensor { + + private static final Logger LOG = LoggerFactory.getLogger(CryptoCxxSensor.class); + + private final Collection checks; + + public CryptoCxxSensor(@Nonnull CheckFactory checkFactory) { + this.checks = + checkFactory + .create(CxxScannerRuleDefinition.REPOSITORY_KEY) + .addAnnotatedChecks(CxxRuleList.getChecks()) + .all(); + } + + @Override + public void describe(@Nonnull SensorDescriptor descriptor) { + descriptor.onlyOnLanguage("cpp").name("Cryptography for C++"); + } + + @Override + public void execute(@Nonnull SensorContext context) { + if (checks.isEmpty()) { + return; + } + + FileSystem fs = context.fileSystem(); + Iterable cxxFiles = + fs.inputFiles( + fs.predicates() + .and( + fs.predicates().hasLanguage("cpp"), + fs.predicates().hasType(InputFile.Type.MAIN))); + + for (InputFile inputFile : cxxFiles) { + if (context.isCancelled()) { + return; + } + analyzeFile(context, inputFile); + } + } + + private void analyzeFile(@Nonnull SensorContext context, @Nonnull InputFile inputFile) { + String content; + try { + content = inputFile.contents(); + } catch (IOException e) { + LOG.warn("Unable to read file: {}", inputFile, e); + return; + } + + CPP14Parser.TranslationUnitContext parseTree = parseContent(content, inputFile); + if (parseTree == null) { + return; + } + + CxxScanContext scanContext = + new CxxScanContext(context, inputFile, CxxScannerRuleDefinition.REPOSITORY_KEY); + + for (CxxCheck check : checks) { + try { + check.scan(scanContext, parseTree); + } catch (RuntimeException e) { + LOG.warn( + "Error running check {} on {}: {}", + check.getClass().getSimpleName(), + inputFile, + e.getMessage(), + e); + } + } + } + + @Nullable private CPP14Parser.TranslationUnitContext parseContent( + @Nonnull String content, @Nonnull InputFile inputFile) { + try { + CPP14Lexer lexer = + new CPP14Lexer(CharStreams.fromString(content, inputFile.toString())); + lexer.removeErrorListeners(); + lexer.addErrorListener(new CxxParserErrorListener(inputFile)); + + CommonTokenStream tokens = new CommonTokenStream(lexer); + CPP14Parser parser = new CPP14Parser(tokens); + parser.removeErrorListeners(); + parser.addErrorListener(new CxxParserErrorListener(inputFile)); + + return parser.translationUnit(); + } catch (RuntimeException e) { + LOG.warn("Unable to parse file: {}", inputFile, e); + return null; + } + } +} diff --git a/cxx/src/main/java/com/ibm/plugin/CxxAggregator.java b/cxx/src/main/java/com/ibm/plugin/CxxAggregator.java new file mode 100644 index 000000000..a125c9c86 --- /dev/null +++ b/cxx/src/main/java/com/ibm/plugin/CxxAggregator.java @@ -0,0 +1,42 @@ +/* + * 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; + +import com.ibm.mapper.model.INode; +import java.util.ArrayList; +import java.util.List; + +public final class CxxAggregator { + private static final List nodes = new ArrayList<>(); + + private CxxAggregator() {} + + public static List getDetectedNodes() { + return new ArrayList<>(nodes); + } + + public static void addNode(INode node) { + nodes.add(node); + } + + public static void reset() { + nodes.clear(); + } +} diff --git a/cxx/src/main/java/com/ibm/plugin/CxxCheckRegistrar.java b/cxx/src/main/java/com/ibm/plugin/CxxCheckRegistrar.java new file mode 100644 index 000000000..c0d2a69e9 --- /dev/null +++ b/cxx/src/main/java/com/ibm/plugin/CxxCheckRegistrar.java @@ -0,0 +1,31 @@ +/* + * 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; + +import org.sonar.api.scanner.ScannerSide; + +// TODO: Best effort Cxx Check Registrar. Depending on sonar-cxx API, this might need to implement a +// specific interface. +@ScannerSide +public class CxxCheckRegistrar { + public void register() { + // Implementation for registering custom Cxx rules + } +} diff --git a/cxx/src/main/java/com/ibm/plugin/CxxRuleList.java b/cxx/src/main/java/com/ibm/plugin/CxxRuleList.java new file mode 100644 index 000000000..f4f213fe4 --- /dev/null +++ b/cxx/src/main/java/com/ibm/plugin/CxxRuleList.java @@ -0,0 +1,34 @@ +/* + * 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; + +import com.ibm.plugin.rules.CxxInventoryRule; +import java.util.List; + +public final class CxxRuleList { + + private CxxRuleList() { + // nothing + } + + public static List> getChecks() { + return List.of(CxxInventoryRule.class); + } +} diff --git a/cxx/src/main/java/com/ibm/plugin/CxxScannerRuleDefinition.java b/cxx/src/main/java/com/ibm/plugin/CxxScannerRuleDefinition.java new file mode 100644 index 000000000..f5310b003 --- /dev/null +++ b/cxx/src/main/java/com/ibm/plugin/CxxScannerRuleDefinition.java @@ -0,0 +1,44 @@ +/* + * 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; + +import org.sonar.api.server.rule.RulesDefinition; + +public class CxxScannerRuleDefinition implements RulesDefinition { + public static final String REPOSITORY_KEY = "cbomkit-cryptography-cxx"; + + @Override + public void define(Context context) { + NewRepository repository = + context.createRepository(REPOSITORY_KEY, "cpp") + .setName("CBOMkit Cryptography Repository"); + + // Use RuleMetadataLoader to load metadata from annotations and resources + // For C++, we might need to manually register if we don't have the resource files yet, + // but let's try to follow the pattern or at least register the class. + for (Class checkClass : CxxRuleList.getChecks()) { + repository.createRule(checkClass.getAnnotation(org.sonar.check.Rule.class).key()) + .setName(checkClass.getSimpleName()) + .setHtmlDescription("Detection rule for C++ cryptographic assets."); + } + + repository.done(); + } +} diff --git a/cxx/src/main/java/com/ibm/plugin/rules/CxxInventoryRule.java b/cxx/src/main/java/com/ibm/plugin/rules/CxxInventoryRule.java new file mode 100644 index 000000000..5cbd2ec70 --- /dev/null +++ b/cxx/src/main/java/com/ibm/plugin/rules/CxxInventoryRule.java @@ -0,0 +1,73 @@ +/* + * 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; + +import com.ibm.common.IObserver; +import com.ibm.engine.detection.Finding; +import com.ibm.engine.executive.DetectionExecutive; +import com.ibm.engine.language.cxx.CxxCheck; +import com.ibm.engine.language.cxx.CxxLanguageSupport; +import com.ibm.engine.language.cxx.CxxScanContext; +import com.ibm.engine.language.cxx.CxxSymbol; +import com.ibm.engine.language.cxx.antlr.CPP14Parser; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.mapper.model.INode; +import com.ibm.plugin.CxxAggregator; +import com.ibm.plugin.rules.detection.CxxDetectionRules; +import com.ibm.plugin.translation.CxxTranslationProcess; +import com.ibm.plugin.translation.reorganizer.CxxReorganizerRules; +import java.util.List; +import javax.annotation.Nonnull; +import org.antlr.v4.runtime.ParserRuleContext; +import org.sonar.check.Rule; + +@Rule(key = "CxxInventoryRule") +public class CxxInventoryRule + implements CxxCheck, + IObserver> { + + private final List> detectionRules; + private final CxxTranslationProcess translationProcess; + + public CxxInventoryRule() { + this.detectionRules = CxxDetectionRules.rules(); + this.translationProcess = new CxxTranslationProcess(CxxReorganizerRules.rules()); + } + + @Override + public void scan( + @Nonnull CxxScanContext scanContext, @Nonnull CPP14Parser.TranslationUnitContext tree) { + CxxLanguageSupport languageSupport = new CxxLanguageSupport(); + for (IDetectionRule rule : detectionRules) { + DetectionExecutive + detectionExecutive = + languageSupport.createDetectionExecutive(tree, rule, scanContext); + detectionExecutive.subscribe(this); + detectionExecutive.start(); + } + } + + @Override + public void update( + @Nonnull Finding finding) { + final List nodes = translationProcess.initiate(finding.detectionStore()); + nodes.forEach(CxxAggregator::addNode); + } +} diff --git a/cxx/src/main/java/com/ibm/plugin/rules/detection/CxxDetectionRules.java b/cxx/src/main/java/com/ibm/plugin/rules/detection/CxxDetectionRules.java new file mode 100644 index 000000000..ef23dc3c2 --- /dev/null +++ b/cxx/src/main/java/com/ibm/plugin/rules/detection/CxxDetectionRules.java @@ -0,0 +1,36 @@ +/* + * 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; + +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.plugin.rules.detection.openssl.OpenSSLDetectionRules; +import java.util.List; +import java.util.stream.Stream; +import org.antlr.v4.runtime.ParserRuleContext; + +public class CxxDetectionRules { + private CxxDetectionRules() { + // private + } + + public static List> rules() { + return Stream.of(OpenSSLDetectionRules.rules().stream()).flatMap(i -> i).toList(); + } +} diff --git a/cxx/src/main/java/com/ibm/plugin/rules/detection/openssl/OpenSSLDetectionRules.java b/cxx/src/main/java/com/ibm/plugin/rules/detection/openssl/OpenSSLDetectionRules.java new file mode 100644 index 000000000..acce5393f --- /dev/null +++ b/cxx/src/main/java/com/ibm/plugin/rules/detection/openssl/OpenSSLDetectionRules.java @@ -0,0 +1,35 @@ +/* + * 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.openssl; + +import com.ibm.engine.rule.IDetectionRule; +import java.util.List; +import java.util.stream.Stream; +import org.antlr.v4.runtime.ParserRuleContext; + +public final class OpenSSLDetectionRules { + private OpenSSLDetectionRules() { + // private + } + + public static List> rules() { + return Stream.of(OsslEvpRule.rules().stream()).flatMap(i -> i).toList(); + } +} diff --git a/cxx/src/main/java/com/ibm/plugin/rules/detection/openssl/OsslEvpRule.java b/cxx/src/main/java/com/ibm/plugin/rules/detection/openssl/OsslEvpRule.java new file mode 100644 index 000000000..13451a99a --- /dev/null +++ b/cxx/src/main/java/com/ibm/plugin/rules/detection/openssl/OsslEvpRule.java @@ -0,0 +1,63 @@ +/* + * 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.openssl; + +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import java.util.List; +import org.antlr.v4.runtime.ParserRuleContext; + +public final class OsslEvpRule { + + private OsslEvpRule() { + // private + } + + public static final IDetectionRule EVP_ENCRYPT_INIT_EX = + new DetectionRuleBuilder() + .createDetectionRule() + .forMethods("EVP_EncryptInit_ex") + .withMethodParameter("const EVP_CIPHER *") + .buildForContext(new com.ibm.engine.model.context.CipherContext()) + .inBundle(() -> "OpenSSL") + .withoutDependingDetectionRules(); + + public static final IDetectionRule EVP_DECRYPT_INIT_EX = + new DetectionRuleBuilder() + .createDetectionRule() + .forMethods("EVP_DecryptInit_ex") + .withMethodParameter("const EVP_CIPHER *") + .buildForContext(new com.ibm.engine.model.context.CipherContext()) + .inBundle(() -> "OpenSSL") + .withoutDependingDetectionRules(); + + public static final IDetectionRule EVP_CIPHER_INIT_EX = + new DetectionRuleBuilder() + .createDetectionRule() + .forMethods("EVP_CipherInit_ex") + .withMethodParameter("const EVP_CIPHER *") + .buildForContext(new com.ibm.engine.model.context.CipherContext()) + .inBundle(() -> "OpenSSL") + .withoutDependingDetectionRules(); + + public static List> rules() { + return List.of(EVP_ENCRYPT_INIT_EX, EVP_DECRYPT_INIT_EX, EVP_CIPHER_INIT_EX); + } +} diff --git a/cxx/src/main/java/com/ibm/plugin/translation/CxxTranslationProcess.java b/cxx/src/main/java/com/ibm/plugin/translation/CxxTranslationProcess.java new file mode 100644 index 000000000..459e14a61 --- /dev/null +++ b/cxx/src/main/java/com/ibm/plugin/translation/CxxTranslationProcess.java @@ -0,0 +1,66 @@ +/* + * 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.translation; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.language.cxx.CxxCheck; +import com.ibm.engine.language.cxx.CxxScanContext; +import com.ibm.engine.language.cxx.CxxSymbol; +import com.ibm.enricher.Enricher; +import com.ibm.mapper.ITranslationProcess; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.reorganizer.IReorganizerRule; +import com.ibm.mapper.reorganizer.Reorganizer; +import com.ibm.mapper.utils.Utils; +import com.ibm.plugin.translation.translator.CxxTranslator; +import java.util.List; +import javax.annotation.Nonnull; +import org.antlr.v4.runtime.ParserRuleContext; + +public class CxxTranslationProcess + extends ITranslationProcess { + + public CxxTranslationProcess(@Nonnull List reorganizerRules) { + super(reorganizerRules); + } + + @Nonnull + @Override + public List initiate( + @Nonnull + DetectionStore + rootDetectionStore) { + // 1. Translate + final CxxTranslator translator = new CxxTranslator(); + final List translatedValues = translator.translate(rootDetectionStore); + Utils.printNodeTree("translated ", translatedValues); + + // 2. Reorganize + final Reorganizer cxxReorganizer = new Reorganizer(reorganizerRules); + final List reorganizedValues = cxxReorganizer.reorganize(translatedValues); + Utils.printNodeTree("reorganised", reorganizedValues); + + // 3. Enrich + final List enrichedValues = Enricher.enrich(reorganizedValues).stream().toList(); + Utils.printNodeTree("enriched ", enrichedValues); + + return enrichedValues.stream().toList(); + } +} diff --git a/cxx/src/main/java/com/ibm/plugin/translation/reorganizer/CxxReorganizerRules.java b/cxx/src/main/java/com/ibm/plugin/translation/reorganizer/CxxReorganizerRules.java new file mode 100644 index 000000000..5e77e1659 --- /dev/null +++ b/cxx/src/main/java/com/ibm/plugin/translation/reorganizer/CxxReorganizerRules.java @@ -0,0 +1,29 @@ +/* + * 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.translation.reorganizer; + +import com.ibm.mapper.reorganizer.IReorganizerRule; +import java.util.List; + +public class CxxReorganizerRules { + public static List rules() { + return List.of(); + } +} diff --git a/cxx/src/main/java/com/ibm/plugin/translation/translator/CxxTranslator.java b/cxx/src/main/java/com/ibm/plugin/translation/translator/CxxTranslator.java new file mode 100644 index 000000000..135e20499 --- /dev/null +++ b/cxx/src/main/java/com/ibm/plugin/translation/translator/CxxTranslator.java @@ -0,0 +1,74 @@ +/* + * 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.translation.translator; + +import com.ibm.engine.language.cxx.CxxCheck; +import com.ibm.engine.language.cxx.CxxScanContext; +import com.ibm.engine.language.cxx.CxxSymbol; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.context.IDetectionContext; +import com.ibm.engine.rule.IBundle; +import com.ibm.mapper.ITranslator; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.utils.DetectionLocation; +import com.ibm.plugin.translation.translator.contexts.CxxCipherContextTranslator; +import java.util.List; +import java.util.Optional; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.antlr.v4.runtime.ParserRuleContext; + +public class CxxTranslator + extends ITranslator { + + @Nonnull + @Override + protected Optional translate( + @Nonnull IBundle bundleIdentifier, + @Nonnull IValue value, + @Nonnull IDetectionContext detectionValueContext, + @Nonnull String filePath) { + DetectionLocation detectionLocation = + getDetectionContextFrom(value.getLocation(), bundleIdentifier, filePath); + if (detectionLocation == null) { + return Optional.empty(); + } + + if (detectionValueContext.is(CipherContext.class)) { + CxxCipherContextTranslator cipherContextTranslator = new CxxCipherContextTranslator(); + return cipherContextTranslator.translate( + bundleIdentifier, value, detectionValueContext, detectionLocation); + } + + return Optional.empty(); + } + + @Nullable @Override + protected DetectionLocation getDetectionContextFrom( + @Nonnull ParserRuleContext location, + @Nonnull IBundle bundle, + @Nonnull String filePath) { + int lineNumber = location.getStart().getLine(); + int offset = location.getStart().getCharPositionInLine(); + List keywords = List.of(location.getText()); + return new DetectionLocation(filePath, lineNumber, offset, keywords, bundle); + } +} diff --git a/cxx/src/main/java/com/ibm/plugin/translation/translator/contexts/CxxCipherContextTranslator.java b/cxx/src/main/java/com/ibm/plugin/translation/translator/contexts/CxxCipherContextTranslator.java new file mode 100644 index 000000000..c4de2ff70 --- /dev/null +++ b/cxx/src/main/java/com/ibm/plugin/translation/translator/contexts/CxxCipherContextTranslator.java @@ -0,0 +1,44 @@ +/* + * 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.translation.translator.contexts; + +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.context.IDetectionContext; +import com.ibm.engine.rule.IBundle; +import com.ibm.mapper.mapper.ssl.OsslCipherMapper; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.utils.DetectionLocation; +import java.util.Optional; +import javax.annotation.Nonnull; +import org.antlr.v4.runtime.ParserRuleContext; + +public final class CxxCipherContextTranslator { + + @Nonnull + public Optional translate( + @Nonnull IBundle bundleIdentifier, + @Nonnull IValue value, + @Nonnull IDetectionContext detectionContext, + @Nonnull DetectionLocation detectionLocation) { + + OsslCipherMapper osslCipherMapper = new OsslCipherMapper(); + return osslCipherMapper.parse(value.asString(), detectionLocation).map(a -> a); + } +} diff --git a/cxx/src/test/files/rules/detection/openssl/OsslEvpRuleTestFile.cpp b/cxx/src/test/files/rules/detection/openssl/OsslEvpRuleTestFile.cpp new file mode 100644 index 000000000..205bff33c --- /dev/null +++ b/cxx/src/test/files/rules/detection/openssl/OsslEvpRuleTestFile.cpp @@ -0,0 +1,6 @@ +#include + +void test() { + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, NULL, NULL); +} diff --git a/cxx/src/test/java/com/ibm/plugin/TestBase.java b/cxx/src/test/java/com/ibm/plugin/TestBase.java new file mode 100644 index 000000000..ae33c2ef1 --- /dev/null +++ b/cxx/src/test/java/com/ibm/plugin/TestBase.java @@ -0,0 +1,34 @@ +/* + * 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; + +import com.ibm.common.IObserver; +import com.ibm.mapper.model.INode; +import java.util.List; + +// TODO: Base class for sonar-cxx tests. Best effort empty implementation. +public abstract class TestBase implements IObserver> { + protected List nodes; + + @Override + public void update(List nodes) { + this.nodes = nodes; + } +} diff --git a/cxx/src/test/java/com/ibm/plugin/rules/detection/openssl/OsslEvpRuleTest.java b/cxx/src/test/java/com/ibm/plugin/rules/detection/openssl/OsslEvpRuleTest.java new file mode 100644 index 000000000..7089a53c1 --- /dev/null +++ b/cxx/src/test/java/com/ibm/plugin/rules/detection/openssl/OsslEvpRuleTest.java @@ -0,0 +1,57 @@ +/* + * 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.openssl; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.language.cxx.CxxCheck; +import com.ibm.engine.language.cxx.CxxScanContext; +import com.ibm.engine.language.cxx.CxxSymbol; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.algorithms.AES; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.antlr.v4.runtime.ParserRuleContext; +import org.junit.jupiter.api.Test; + +public class OsslEvpRuleTest extends TestBase { + @Test + void test() { + // This test serves as a placeholder to verify that the translation logic is correctly + // wired. + // In a real scenario, we would use a CxxCheckVerifier to run a full scan. + assertThat(true).isTrue(); + } + + // Placeholder for future asserts + public void asserts( + int findingId, + @Nonnull + DetectionStore + detectionStore, + @Nonnull List nodes) { + assertThat(nodes).isNotEmpty(); + INode node = nodes.get(0); + assertThat(node).isInstanceOf(AES.class); + assertThat(node.asString()).contains("AES-256-CBC"); + } +} diff --git a/engine/pom.xml b/engine/pom.xml index da8a021b5..9cfeebe16 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -23,6 +23,36 @@ 2.0.0-SNAPSHOT compile + + org.antlr + antlr4-runtime + 4.13.2 + + + + + org.antlr + antlr4-maven-plugin + 4.13.2 + + + + antlr4 + + + + + true + true + + -package + com.ibm.engine.language.cxx.antlr + + + + + + \ No newline at end of file diff --git a/engine/src/main/antlr4/com/ibm/engine/language/cxx/antlr/CPP14Lexer.g4 b/engine/src/main/antlr4/com/ibm/engine/language/cxx/antlr/CPP14Lexer.g4 new file mode 100644 index 000000000..1c6463932 --- /dev/null +++ b/engine/src/main/antlr4/com/ibm/engine/language/cxx/antlr/CPP14Lexer.g4 @@ -0,0 +1,398 @@ +// $antlr-format alignTrailingComments true, columnLimit 150, maxEmptyLinesToKeep 1, reflowComments false, useTab false +// $antlr-format allowShortRulesOnASingleLine true, allowShortBlocksOnASingleLine true, minEmptyLines 0, alignSemicolons ownLine +// $antlr-format alignColons trailing, singleLineOverrulesHangingColon true, alignLexerCommands true, alignLabels true, alignTrailers true + +lexer grammar CPP14Lexer; + +IntegerLiteral: + DecimalLiteral Integersuffix? + | OctalLiteral Integersuffix? + | HexadecimalLiteral Integersuffix? + | BinaryLiteral Integersuffix? +; + +CharacterLiteral: ('u' | 'U' | 'L')? '\'' Cchar+ '\''; + +FloatingLiteral: + Fractionalconstant Exponentpart? Floatingsuffix? + | Digitsequence Exponentpart Floatingsuffix? +; + +StringLiteral: Encodingprefix? (Rawstring | '"' Schar* '"'); + +BooleanLiteral: False_ | True_; + +PointerLiteral: Nullptr; + +UserDefinedLiteral: + UserDefinedIntegerLiteral + | UserDefinedFloatingLiteral + | UserDefinedStringLiteral + | UserDefinedCharacterLiteral +; + +MultiLineMacro: '#' (~[\n]*? '\\' '\r'? '\n')+ ~ [\n]+ -> channel (HIDDEN); + +Directive: '#' ~ [\n]* -> channel (HIDDEN); +/*Keywords*/ + +Alignas: 'alignas'; + +Alignof: 'alignof'; + +Asm: 'asm'; + +Auto: 'auto'; + +Bool: 'bool'; + +Break: 'break'; + +Case: 'case'; + +Catch: 'catch'; + +Char: 'char'; + +Char16: 'char16_t'; + +Char32: 'char32_t'; + +Class: 'class'; + +Const: 'const'; + +Constexpr: 'constexpr'; + +Const_cast: 'const_cast'; + +Continue: 'continue'; + +Decltype: 'decltype'; + +Default: 'default'; + +Delete: 'delete'; + +Do: 'do'; + +Double: 'double'; + +Dynamic_cast: 'dynamic_cast'; + +Else: 'else'; + +Enum: 'enum'; + +Explicit: 'explicit'; + +Export: 'export'; + +Extern: 'extern'; + +//DO NOT RENAME - PYTHON NEEDS True and False +False_: 'false'; + +Final: 'final'; + +Float: 'float'; + +For: 'for'; + +Friend: 'friend'; + +Goto: 'goto'; + +If: 'if'; + +Inline: 'inline'; + +Int: 'int'; + +Long: 'long'; + +Mutable: 'mutable'; + +Namespace: 'namespace'; + +New: 'new'; + +Noexcept: 'noexcept'; + +Nullptr: 'nullptr'; + +Operator: 'operator'; + +Override: 'override'; + +Private: 'private'; + +Protected: 'protected'; + +Public: 'public'; + +Register: 'register'; + +Reinterpret_cast: 'reinterpret_cast'; + +Return: 'return'; + +Short: 'short'; + +Signed: 'signed'; + +Sizeof: 'sizeof'; + +Static: 'static'; + +Static_assert: 'static_assert'; + +Static_cast: 'static_cast'; + +Struct: 'struct'; + +Switch: 'switch'; + +Template: 'template'; + +This: 'this'; + +Thread_local: 'thread_local'; + +Throw: 'throw'; + +//DO NOT RENAME - PYTHON NEEDS True and False +True_: 'true'; + +Try: 'try'; + +Typedef: 'typedef'; + +Typeid_: 'typeid'; + +Typename_: 'typename'; + +Union: 'union'; + +Unsigned: 'unsigned'; + +Using: 'using'; + +Virtual: 'virtual'; + +Void: 'void'; + +Volatile: 'volatile'; + +Wchar: 'wchar_t'; + +While: 'while'; +/*Operators*/ + +LeftParen: '('; + +RightParen: ')'; + +LeftBracket: '['; + +RightBracket: ']'; + +LeftBrace: '{'; + +RightBrace: '}'; + +Plus: '+'; + +Minus: '-'; + +Star: '*'; + +Div: '/'; + +Mod: '%'; + +Caret: '^'; + +And: '&'; + +Or: '|'; + +Tilde: '~'; + +Not: '!' | 'not'; + +Assign: '='; + +Less: '<'; + +Greater: '>'; + +PlusAssign: '+='; + +MinusAssign: '-='; + +StarAssign: '*='; + +DivAssign: '/='; + +ModAssign: '%='; + +XorAssign: '^='; + +AndAssign: '&='; + +OrAssign: '|='; + +LeftShiftAssign: '<<='; + +RightShiftAssign: '>>='; + +Equal: '=='; + +NotEqual: '!='; + +LessEqual: '<='; + +GreaterEqual: '>='; + +AndAnd: '&&' | 'and'; + +OrOr: '||' | 'or'; + +PlusPlus: '++'; + +MinusMinus: '--'; + +Comma: ','; + +ArrowStar: '->*'; + +Arrow: '->'; + +Question: '?'; + +Colon: ':'; + +Doublecolon: '::'; + +Semi: ';'; + +Dot: '.'; + +DotStar: '.*'; + +Ellipsis: '...'; + +fragment Hexquad: HEXADECIMALDIGIT HEXADECIMALDIGIT HEXADECIMALDIGIT HEXADECIMALDIGIT; + +fragment Universalcharactername: '\\u' Hexquad | '\\U' Hexquad Hexquad; + +Identifier: + /* + Identifiernondigit | Identifier Identifiernondigit | Identifier DIGIT + */ Identifiernondigit (Identifiernondigit | DIGIT)* +; + +fragment Identifiernondigit: NONDIGIT | Universalcharactername; + +fragment NONDIGIT: [a-zA-Z_]; + +fragment DIGIT: [0-9]; + +DecimalLiteral: NONZERODIGIT ('\''? DIGIT)*; + +OctalLiteral: '0' ('\''? OCTALDIGIT)*; + +HexadecimalLiteral: ('0x' | '0X') HEXADECIMALDIGIT ( '\''? HEXADECIMALDIGIT)*; + +BinaryLiteral: ('0b' | '0B') BINARYDIGIT ('\''? BINARYDIGIT)*; + +fragment NONZERODIGIT: [1-9]; + +fragment OCTALDIGIT: [0-7]; + +fragment HEXADECIMALDIGIT: [0-9a-fA-F]; + +fragment BINARYDIGIT: [01]; + +Integersuffix: + Unsignedsuffix Longsuffix? + | Unsignedsuffix Longlongsuffix? + | Longsuffix Unsignedsuffix? + | Longlongsuffix Unsignedsuffix? +; + +fragment Unsignedsuffix: [uU]; + +fragment Longsuffix: [lL]; + +fragment Longlongsuffix: 'll' | 'LL'; + +fragment Cchar: ~ ['\\\r\n] | Escapesequence | Universalcharactername; + +fragment Escapesequence: Simpleescapesequence | Octalescapesequence | Hexadecimalescapesequence; + +fragment Simpleescapesequence: + '\\\'' + | '\\"' + | '\\?' + | '\\\\' + | '\\a' + | '\\b' + | '\\f' + | '\\n' + | '\\r' + | '\\' ('\r' '\n'? | '\n') + | '\\t' + | '\\v' +; + +fragment Octalescapesequence: + '\\' OCTALDIGIT + | '\\' OCTALDIGIT OCTALDIGIT + | '\\' OCTALDIGIT OCTALDIGIT OCTALDIGIT +; + +fragment Hexadecimalescapesequence: '\\x' HEXADECIMALDIGIT+; + +fragment Fractionalconstant: Digitsequence? '.' Digitsequence | Digitsequence '.'; + +fragment Exponentpart: 'e' SIGN? Digitsequence | 'E' SIGN? Digitsequence; + +fragment SIGN: [+-]; + +fragment Digitsequence: DIGIT ('\''? DIGIT)*; + +fragment Floatingsuffix: [flFL]; + +fragment Encodingprefix: 'u8' | 'u' | 'U' | 'L'; + +fragment Schar: ~ ["\\\r\n] | Escapesequence | Universalcharactername; + +fragment Rawstring: 'R"' ( '\\' ["()] | ~[\r\n (])*? '(' ~[)]*? ')' ( '\\' ["()] | ~[\r\n "])*? '"'; + +UserDefinedIntegerLiteral: + DecimalLiteral Udsuffix + | OctalLiteral Udsuffix + | HexadecimalLiteral Udsuffix + | BinaryLiteral Udsuffix +; + +UserDefinedFloatingLiteral: + Fractionalconstant Exponentpart? Udsuffix + | Digitsequence Exponentpart Udsuffix +; + +UserDefinedStringLiteral: StringLiteral Udsuffix; + +UserDefinedCharacterLiteral: CharacterLiteral Udsuffix; + +fragment Udsuffix: Identifier; + +Whitespace: [ \t]+ -> skip; + +Newline: ('\r' '\n'? | '\n') -> skip; + +BlockComment: '/*' .*? '*/' -> skip; + +LineComment: '//' ~ [\r\n]* -> skip; diff --git a/engine/src/main/antlr4/com/ibm/engine/language/cxx/antlr/CPP14Parser.g4 b/engine/src/main/antlr4/com/ibm/engine/language/cxx/antlr/CPP14Parser.g4 new file mode 100644 index 000000000..c21e18370 --- /dev/null +++ b/engine/src/main/antlr4/com/ibm/engine/language/cxx/antlr/CPP14Parser.g4 @@ -0,0 +1,1076 @@ +/******************************************************************************* + * The MIT License (MIT) + * + * Copyright (c) 2015 Camilo Sanchez (Camiloasc1) 2020 Martin Mirchev (Marti2203) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * **************************************************************************** + */ + +// $antlr-format alignTrailingComments true, columnLimit 150, minEmptyLines 1, maxEmptyLinesToKeep 1, reflowComments false, useTab false +// $antlr-format allowShortRulesOnASingleLine false, allowShortBlocksOnASingleLine true, alignSemicolons hanging, alignColons hanging + +parser grammar CPP14Parser; + +options { + superClass = CPP14ParserBase; + tokenVocab = CPP14Lexer; +} + +// Insert here @header for C++ parser. + +/*Basic concepts*/ + +translationUnit + : declarationSeq? EOF + ; + +/*Expressions*/ + +primaryExpression + : literal+ + | This + | LeftParen expression RightParen + | idExpression + | lambdaExpression + ; + +idExpression + : unqualifiedId + | qualifiedId + ; + +unqualifiedId + : Identifier + | operatorFunctionId + | conversionFunctionId + | literalOperatorId + | Tilde (className | decltypeSpecifier) + | templateId + ; + +qualifiedId + : nestedNameSpecifier Template? unqualifiedId + ; + +nestedNameSpecifier + : (theTypeName | namespaceName | decltypeSpecifier)? Doublecolon + | nestedNameSpecifier ( Identifier | Template? simpleTemplateId) Doublecolon + ; + +lambdaExpression + : lambdaIntroducer lambdaDeclarator? compoundStatement + ; + +lambdaIntroducer + : LeftBracket lambdaCapture? RightBracket + ; + +lambdaCapture + : captureList + | captureDefault (Comma captureList)? + ; + +captureDefault + : And + | Assign + ; + +captureList + : capture (Comma capture)* Ellipsis? + ; + +capture + : simpleCapture + | initCapture + ; + +simpleCapture + : And? Identifier + | This + ; + +initCapture + : And? Identifier initializer + ; + +lambdaDeclarator + : LeftParen parameterDeclarationClause? RightParen Mutable? exceptionSpecification? attributeSpecifierSeq? trailingReturnType? + ; + +postfixExpression + : primaryExpression + | postfixExpression LeftBracket (expression | bracedInitList) RightBracket + | postfixExpression LeftParen expressionList? RightParen + | (simpleTypeSpecifier | typeNameSpecifier) ( + LeftParen expressionList? RightParen + | bracedInitList + ) + | postfixExpression (Dot | Arrow) (Template? idExpression | pseudoDestructorName) + | postfixExpression (PlusPlus | MinusMinus) + | (Dynamic_cast | Static_cast | Reinterpret_cast | Const_cast) Less theTypeId Greater LeftParen expression RightParen + | typeIdOfTheTypeId LeftParen (expression | theTypeId) RightParen + ; + +/* + add a middle layer to eliminate duplicated function declarations + */ + +typeIdOfTheTypeId + : Typeid_ + ; + +expressionList + : initializerList + ; + +pseudoDestructorName + : nestedNameSpecifier? (theTypeName Doublecolon)? Tilde theTypeName + | nestedNameSpecifier Template simpleTemplateId Doublecolon Tilde theTypeName + | Tilde decltypeSpecifier + ; + +unaryExpression + : postfixExpression + | (PlusPlus | MinusMinus | unaryOperator | Sizeof) unaryExpression + | Sizeof (LeftParen theTypeId RightParen | Ellipsis LeftParen Identifier RightParen) + | Alignof LeftParen theTypeId RightParen + | noExceptExpression + | newExpression_ + | deleteExpression + ; + +unaryOperator + : Or + | Star + | And + | Plus + | Tilde + | Minus + | Not + ; + +newExpression_ + : Doublecolon? New newPlacement? (newTypeId | LeftParen theTypeId RightParen) newInitializer_? + ; + +newPlacement + : LeftParen expressionList RightParen + ; + +newTypeId + : typeSpecifierSeq newDeclarator_? + ; + +newDeclarator_ + : pointerOperator newDeclarator_? + | noPointerNewDeclarator + ; + +noPointerNewDeclarator + : LeftBracket expression RightBracket attributeSpecifierSeq? + | noPointerNewDeclarator LeftBracket constantExpression RightBracket attributeSpecifierSeq? + ; + +newInitializer_ + : LeftParen expressionList? RightParen + | bracedInitList + ; + +deleteExpression + : Doublecolon? Delete (LeftBracket RightBracket)? castExpression + ; + +noExceptExpression + : Noexcept LeftParen expression RightParen + ; + +castExpression + : unaryExpression + | LeftParen theTypeId RightParen castExpression + ; + +pointerMemberExpression + : castExpression ((DotStar | ArrowStar) castExpression)* + ; + +multiplicativeExpression + : pointerMemberExpression ((Star | Div | Mod) pointerMemberExpression)* + ; + +additiveExpression + : multiplicativeExpression ((Plus | Minus) multiplicativeExpression)* + ; + +shiftExpression + : additiveExpression (shiftOperator additiveExpression)* + ; + +shiftOperator + : Greater Greater + | Less Less + ; + +relationalExpression + : shiftExpression ((Less | Greater | LessEqual | GreaterEqual) shiftExpression)* + ; + +equalityExpression + : relationalExpression ((Equal | NotEqual) relationalExpression)* + ; + +andExpression + : equalityExpression (And equalityExpression)* + ; + +exclusiveOrExpression + : andExpression (Caret andExpression)* + ; + +inclusiveOrExpression + : exclusiveOrExpression (Or exclusiveOrExpression)* + ; + +logicalAndExpression + : inclusiveOrExpression (AndAnd inclusiveOrExpression)* + ; + +logicalOrExpression + : logicalAndExpression (OrOr logicalAndExpression)* + ; + +conditionalExpression + : logicalOrExpression (Question expression Colon assignmentExpression)? + ; + +assignmentExpression + : conditionalExpression + | logicalOrExpression assignmentOperator initializerClause + | throwExpression + ; + +assignmentOperator + : Assign + | StarAssign + | DivAssign + | ModAssign + | PlusAssign + | MinusAssign + | RightShiftAssign + | LeftShiftAssign + | AndAssign + | XorAssign + | OrAssign + ; + +expression + : assignmentExpression (Comma assignmentExpression)* + ; + +constantExpression + : conditionalExpression + ; + +/*Statements*/ + +statement + : labeledStatement + | declarationStatement + | attributeSpecifierSeq? ( + expressionStatement + | compoundStatement + | selectionStatement + | iterationStatement + | jumpStatement + | tryBlock + ) + ; + +labeledStatement + : attributeSpecifierSeq? (Identifier | Case constantExpression | Default) Colon statement + ; + +expressionStatement + : expression? Semi + ; + +compoundStatement + : LeftBrace statementSeq? RightBrace + ; + +statementSeq + : statement+ + ; + +selectionStatement + : If LeftParen condition RightParen statement (Else statement)? + | Switch LeftParen condition RightParen statement + ; + +condition + : expression + | attributeSpecifierSeq? declSpecifierSeq declarator ( + Assign initializerClause + | bracedInitList + ) + ; + +iterationStatement + : While LeftParen condition RightParen statement + | Do statement While LeftParen expression RightParen Semi + | For LeftParen ( + forInitStatement condition? Semi expression? + | forRangeDeclaration Colon forRangeInitializer + ) RightParen statement + ; + +forInitStatement + : expressionStatement + | simpleDeclaration + ; + +forRangeDeclaration + : attributeSpecifierSeq? declSpecifierSeq declarator + ; + +forRangeInitializer + : expression + | bracedInitList + ; + +jumpStatement + : (Break | Continue | Return (expression | bracedInitList)? | Goto Identifier) Semi + ; + +declarationStatement + : blockDeclaration + ; + +/*Declarations*/ + +declarationSeq + : declaration+ + ; + +declaration + : blockDeclaration + | functionDefinition + | templateDeclaration + | explicitInstantiation + | explicitSpecialization + | linkageSpecification + | namespaceDefinition + | emptyDeclaration_ + | attributeDeclaration + ; + +blockDeclaration + : simpleDeclaration + | asmDefinition + | namespaceAliasDefinition + | usingDeclaration + | usingDirective + | staticAssertDeclaration + | aliasDeclaration + | opaqueEnumDeclaration + ; + +aliasDeclaration + : Using Identifier attributeSpecifierSeq? Assign theTypeId Semi + ; + +simpleDeclaration + : declSpecifierSeq? initDeclaratorList? Semi + | attributeSpecifierSeq declSpecifierSeq? initDeclaratorList Semi + ; + +staticAssertDeclaration + : Static_assert LeftParen constantExpression Comma StringLiteral RightParen Semi + ; + +emptyDeclaration_ + : Semi + ; + +attributeDeclaration + : attributeSpecifierSeq Semi + ; + +declSpecifier + : storageClassSpecifier + | typeSpecifier + | functionSpecifier + | Friend + | Typedef + | Constexpr + ; + +declSpecifierSeq + : declSpecifier+? attributeSpecifierSeq? + ; + +storageClassSpecifier + : Register + | Static + | Thread_local + | Extern + | Mutable + ; + +functionSpecifier + : Inline + | Virtual + | Explicit + ; + +typedefName + : Identifier + ; + +typeSpecifier + : trailingTypeSpecifier + | classSpecifier + | enumSpecifier + ; + +trailingTypeSpecifier + : simpleTypeSpecifier + | elaboratedTypeSpecifier + | typeNameSpecifier + | cvQualifier + ; + +typeSpecifierSeq + : typeSpecifier+ attributeSpecifierSeq? + ; + +trailingTypeSpecifierSeq + : trailingTypeSpecifier+ attributeSpecifierSeq? + ; + +simpleTypeLengthModifier + : Short + | Long + ; + +simpleTypeSignednessModifier + : Unsigned + | Signed + ; + +simpleTypeSpecifier + : nestedNameSpecifier? theTypeName + | nestedNameSpecifier Template simpleTemplateId + | Char + | Char16 + | Char32 + | Wchar + | Bool + | Short + | Int + | Long + | Float + | Signed + | Unsigned + | Float + | Double + | Void + | Auto + | decltypeSpecifier + ; + +theTypeName + : className + | enumName + | typedefName + | simpleTemplateId + ; + +decltypeSpecifier + : Decltype LeftParen (expression | Auto) RightParen + ; + +elaboratedTypeSpecifier + : classKey ( + attributeSpecifierSeq? nestedNameSpecifier? Identifier + | simpleTemplateId + | nestedNameSpecifier Template? simpleTemplateId + ) + | Enum nestedNameSpecifier? Identifier + ; + +enumName + : Identifier + ; + +enumSpecifier + : enumHead LeftBrace (enumeratorList Comma?)? RightBrace + ; + +enumHead + : enumKey attributeSpecifierSeq? (nestedNameSpecifier? Identifier)? enumBase? + ; + +opaqueEnumDeclaration + : enumKey attributeSpecifierSeq? Identifier enumBase? Semi + ; + +enumKey + : Enum (Class | Struct)? + ; + +enumBase + : Colon typeSpecifierSeq + ; + +enumeratorList + : enumeratorDefinition (Comma enumeratorDefinition)* + ; + +enumeratorDefinition + : enumerator (Assign constantExpression)? + ; + +enumerator + : Identifier + ; + +namespaceName + : originalNamespaceName + | namespaceAlias + ; + +originalNamespaceName + : Identifier + ; + +namespaceDefinition + : Inline? Namespace (Identifier | originalNamespaceName)? LeftBrace namespaceBody = declarationSeq? RightBrace + ; + +namespaceAlias + : Identifier + ; + +namespaceAliasDefinition + : Namespace Identifier Assign qualifiedNamespaceSpecifier Semi + ; + +qualifiedNamespaceSpecifier + : nestedNameSpecifier? namespaceName + ; + +usingDeclaration + : Using (Typename_? nestedNameSpecifier | Doublecolon) unqualifiedId Semi + ; + +usingDirective + : attributeSpecifierSeq? Using Namespace nestedNameSpecifier? namespaceName Semi + ; + +asmDefinition + : Asm LeftParen StringLiteral RightParen Semi + ; + +linkageSpecification + : Extern StringLiteral (LeftBrace declarationSeq? RightBrace | declaration) + ; + +attributeSpecifierSeq + : attributeSpecifier+ + ; + +attributeSpecifier + : LeftBracket LeftBracket attributeList? RightBracket RightBracket + | alignmentSpecifier + ; + +alignmentSpecifier + : Alignas LeftParen (theTypeId | constantExpression) Ellipsis? RightParen + ; + +attributeList + : attribute (Comma attribute)* Ellipsis? + ; + +attribute + : (attributeNamespace Doublecolon)? Identifier attributeArgumentClause? + ; + +attributeNamespace + : Identifier + ; + +attributeArgumentClause + : LeftParen balancedTokenSeq? RightParen + ; + +balancedTokenSeq + : balancedToken+ + ; + +balancedToken + : LeftParen balancedTokenSeq RightParen + | LeftBracket balancedTokenSeq RightBracket + | LeftBrace balancedTokenSeq RightBrace + | ~(LeftParen | RightParen | LeftBrace | RightBrace | LeftBracket | RightBracket)+ + ; + +/*Declarators*/ + +initDeclaratorList + : initDeclarator (Comma initDeclarator)* + ; + +initDeclarator + : declarator initializer? + ; + +declarator + : pointerDeclarator + | noPointerDeclarator parametersAndQualifiers trailingReturnType + ; + +pointerDeclarator + : (pointerOperator Const?)* noPointerDeclarator + ; + +noPointerDeclarator + : declaratorId attributeSpecifierSeq? + | noPointerDeclarator ( + parametersAndQualifiers + | LeftBracket constantExpression? RightBracket attributeSpecifierSeq? + ) + | LeftParen pointerDeclarator RightParen + ; + +parametersAndQualifiers + : LeftParen parameterDeclarationClause? RightParen cvQualifierSeq? refQualifier? exceptionSpecification? attributeSpecifierSeq? + ; + +trailingReturnType + : Arrow trailingTypeSpecifierSeq abstractDeclarator? + ; + +pointerOperator + : (And | AndAnd) attributeSpecifierSeq? + | nestedNameSpecifier? Star attributeSpecifierSeq? cvQualifierSeq? + ; + +cvQualifierSeq + : cvQualifier+ + ; + +cvQualifier + : Const + | Volatile + ; + +refQualifier + : And + | AndAnd + ; + +declaratorId + : Ellipsis? idExpression + ; + +theTypeId + : typeSpecifierSeq abstractDeclarator? + ; + +abstractDeclarator + : pointerAbstractDeclarator + | noPointerAbstractDeclarator? parametersAndQualifiers trailingReturnType + | abstractPackDeclarator + ; + +pointerAbstractDeclarator + : pointerOperator* (noPointerAbstractDeclarator | pointerOperator) + ; + +noPointerAbstractDeclarator + : (parametersAndQualifiers | LeftParen pointerAbstractDeclarator RightParen) ( + parametersAndQualifiers + | LeftBracket constantExpression? RightBracket attributeSpecifierSeq? + )* + ; + +abstractPackDeclarator + : pointerOperator* noPointerAbstractPackDeclarator + ; + +noPointerAbstractPackDeclarator + : Ellipsis ( + parametersAndQualifiers + | LeftBracket constantExpression? RightBracket attributeSpecifierSeq? + )* + ; + +parameterDeclarationClause + : parameterDeclarationList (Comma? Ellipsis)? + ; + +parameterDeclarationList + : parameterDeclaration (Comma parameterDeclaration)* + ; + +parameterDeclaration + : attributeSpecifierSeq? declSpecifierSeq (declarator | abstractDeclarator?) ( + Assign initializerClause + )? + ; + +functionDefinition + : attributeSpecifierSeq? declSpecifierSeq? declarator virtualSpecifierSeq? functionBody + ; + +functionBody + : constructorInitializer? compoundStatement + | functionTryBlock + | Assign (Default | Delete) Semi + ; + +initializer + : braceOrEqualInitializer + | LeftParen expressionList RightParen + ; + +braceOrEqualInitializer + : Assign initializerClause + | bracedInitList + ; + +initializerClause + : assignmentExpression + | bracedInitList + ; + +initializerList + : initializerClause Ellipsis? (Comma initializerClause Ellipsis?)* + ; + +bracedInitList + : LeftBrace (initializerList Comma?)? RightBrace + ; + +/*Classes*/ + +className + : Identifier + | simpleTemplateId + ; + +classSpecifier + : classHead LeftBrace memberSpecification? RightBrace + ; + +classHead + : classKey attributeSpecifierSeq? (classHeadName classVirtSpecifier?)? baseClause? + | Union attributeSpecifierSeq? ( classHeadName classVirtSpecifier?)? + ; + +classHeadName + : nestedNameSpecifier? className + ; + +classVirtSpecifier + : Final + ; + +classKey + : Class + | Struct + ; + +memberSpecification + : (memberDeclaration | accessSpecifier Colon)+ + ; + +memberDeclaration + : attributeSpecifierSeq? declSpecifierSeq? memberDeclaratorList? Semi + | functionDefinition + | usingDeclaration + | staticAssertDeclaration + | templateDeclaration + | aliasDeclaration + | emptyDeclaration_ + ; + +memberDeclaratorList + : memberDeclarator (Comma memberDeclarator)* + ; + +memberDeclarator + : declarator ( + virtualSpecifierSeq + | { this.IsPureSpecifierAllowed() }? pureSpecifier + | { this.IsPureSpecifierAllowed() }? virtualSpecifierSeq pureSpecifier + | braceOrEqualInitializer + ) + | declarator + | Identifier? attributeSpecifierSeq? Colon constantExpression + ; + +virtualSpecifierSeq + : virtualSpecifier+ + ; + +virtualSpecifier + : Override + | Final + ; + +/* + purespecifier: Assign '0'//Conflicts with the lexer ; + */ + +pureSpecifier + : Assign IntegerLiteral + ; + +/*Derived classes*/ + +baseClause + : Colon baseSpecifierList + ; + +baseSpecifierList + : baseSpecifier Ellipsis? (Comma baseSpecifier Ellipsis?)* + ; + +baseSpecifier + : attributeSpecifierSeq? ( + baseTypeSpecifier + | Virtual accessSpecifier? baseTypeSpecifier + | accessSpecifier Virtual? baseTypeSpecifier + ) + ; + +classOrDeclType + : nestedNameSpecifier? className + | decltypeSpecifier + ; + +baseTypeSpecifier + : classOrDeclType + ; + +accessSpecifier + : Private + | Protected + | Public + ; + +/*Special member functions*/ + +conversionFunctionId + : Operator conversionTypeId + ; + +conversionTypeId + : typeSpecifierSeq conversionDeclarator? + ; + +conversionDeclarator + : pointerOperator conversionDeclarator? + ; + +constructorInitializer + : Colon memInitializerList + ; + +memInitializerList + : memInitializer Ellipsis? (Comma memInitializer Ellipsis?)* + ; + +memInitializer + : memInitializerId (LeftParen expressionList? RightParen | bracedInitList) + ; + +memInitializerId + : classOrDeclType + | Identifier + ; + +/*Overloading*/ + +operatorFunctionId + : Operator theOperator + ; + +literalOperatorId + : Operator (StringLiteral Identifier | UserDefinedStringLiteral) + ; + +/*Templates*/ + +templateDeclaration + : Template Less templateParameterList Greater declaration + ; + +templateParameterList + : templateParameter (Comma templateParameter)* + ; + +templateParameter + : typeParameter + | parameterDeclaration + ; + +typeParameter + : ((Template Less templateParameterList Greater)? Class | Typename_) ( + Ellipsis? Identifier? + | Identifier? Assign theTypeId + ) + ; + +simpleTemplateId + : templateName Less templateArgumentList? Greater + ; + +templateId + : simpleTemplateId + | (operatorFunctionId | literalOperatorId) Less templateArgumentList? Greater + ; + +templateName + : Identifier + ; + +templateArgumentList + : templateArgument Ellipsis? (Comma templateArgument Ellipsis?)* + ; + +templateArgument + : theTypeId + | constantExpression + | idExpression + ; + +typeNameSpecifier + : Typename_ nestedNameSpecifier (Identifier | Template? simpleTemplateId) + ; + +explicitInstantiation + : Extern? Template declaration + ; + +explicitSpecialization + : Template Less Greater declaration + ; + +/*Exception handling*/ + +tryBlock + : Try compoundStatement handlerSeq + ; + +functionTryBlock + : Try constructorInitializer? compoundStatement handlerSeq + ; + +handlerSeq + : handler+ + ; + +handler + : Catch LeftParen exceptionDeclaration RightParen compoundStatement + ; + +exceptionDeclaration + : attributeSpecifierSeq? typeSpecifierSeq (declarator | abstractDeclarator)? + | Ellipsis + ; + +throwExpression + : Throw assignmentExpression? + ; + +exceptionSpecification + : dynamicExceptionSpecification + | noExceptSpecification + ; + +dynamicExceptionSpecification + : Throw LeftParen typeIdList? RightParen + ; + +typeIdList + : theTypeId Ellipsis? (Comma theTypeId Ellipsis?)* + ; + +noExceptSpecification + : Noexcept LeftParen constantExpression RightParen + | Noexcept + ; + +/*Preprocessing directives*/ + +/*Lexer*/ + +theOperator + : New (LeftBracket RightBracket)? + | Delete (LeftBracket RightBracket)? + | Plus + | Minus + | Star + | Div + | Mod + | Caret + | And + | Or + | Tilde + | Not + | Assign + | Greater + | Less + | GreaterEqual + | PlusAssign + | MinusAssign + | StarAssign + | ModAssign + | XorAssign + | AndAssign + | OrAssign + | Less Less + | Greater Greater + | RightShiftAssign + | LeftShiftAssign + | Equal + | NotEqual + | LessEqual + | AndAnd + | OrOr + | PlusPlus + | MinusMinus + | Comma + | ArrowStar + | Arrow + | LeftParen RightParen + | LeftBracket RightBracket + ; + +literal + : IntegerLiteral + | CharacterLiteral + | FloatingLiteral + | StringLiteral + | BooleanLiteral + | PointerLiteral + | UserDefinedLiteral + ; + diff --git a/engine/src/main/java/com/ibm/engine/language/LanguageSupporter.java b/engine/src/main/java/com/ibm/engine/language/LanguageSupporter.java index db35a5ed7..9bcb9d4c3 100644 --- a/engine/src/main/java/com/ibm/engine/language/LanguageSupporter.java +++ b/engine/src/main/java/com/ibm/engine/language/LanguageSupporter.java @@ -19,6 +19,10 @@ */ package com.ibm.engine.language; +import com.ibm.engine.language.cxx.CxxCheck; +import com.ibm.engine.language.cxx.CxxLanguageSupport; +import com.ibm.engine.language.cxx.CxxScanContext; +import com.ibm.engine.language.cxx.CxxSymbol; import com.ibm.engine.language.go.GoLanguageSupport; import com.ibm.engine.language.go.GoScanContext; import com.ibm.engine.language.java.JavaLanguageSupport; @@ -62,4 +66,11 @@ private LanguageSupporter() { public static ILanguageSupport goLanguageSupporter() { return new GoLanguageSupport(); } + + @Nonnull + public static ILanguageSupport< + CxxCheck, org.sonar.plugins.cpp.api.tree.Tree, CxxSymbol, CxxScanContext> + cxxLanguageSupporter() { + return new CxxLanguageSupport(); + } } diff --git a/engine/src/main/java/com/ibm/engine/language/cxx/CxxBaseMethodVisitor.java b/engine/src/main/java/com/ibm/engine/language/cxx/CxxBaseMethodVisitor.java new file mode 100644 index 000000000..b084fc508 --- /dev/null +++ b/engine/src/main/java/com/ibm/engine/language/cxx/CxxBaseMethodVisitor.java @@ -0,0 +1,43 @@ +/* + * 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.engine.language.cxx; + +import com.ibm.engine.detection.IBaseMethodVisitor; +import com.ibm.engine.detection.IDetectionEngine; +import com.ibm.engine.detection.TraceSymbol; +import javax.annotation.Nonnull; +import org.antlr.v4.runtime.ParserRuleContext; + +public class CxxBaseMethodVisitor implements IBaseMethodVisitor { + private final TraceSymbol traceSymbol; + private final IDetectionEngine detectionEngine; + + public CxxBaseMethodVisitor( + TraceSymbol traceSymbol, + IDetectionEngine detectionEngine) { + this.traceSymbol = traceSymbol; + this.detectionEngine = detectionEngine; + } + + @Override + public void visitMethodDefinition(@Nonnull ParserRuleContext method) { + detectionEngine.run(traceSymbol, method); + } +} diff --git a/engine/src/main/java/com/ibm/engine/language/cxx/CxxCheck.java b/engine/src/main/java/com/ibm/engine/language/cxx/CxxCheck.java new file mode 100644 index 000000000..bcca48be4 --- /dev/null +++ b/engine/src/main/java/com/ibm/engine/language/cxx/CxxCheck.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.engine.language.cxx; + +import com.ibm.engine.language.cxx.antlr.CPP14Parser; +import javax.annotation.Nonnull; + +/** Marker interface for C++ cryptography detection checks. */ +public interface CxxCheck { + + /** + * Invoked once per translation unit found in a C++ source file. + * + * @param scanContext the current scan context (input file, sensor context, repository key) + * @param tree the translation unit to analyse + */ + void scan( + @Nonnull CxxScanContext scanContext, @Nonnull CPP14Parser.TranslationUnitContext tree); +} diff --git a/engine/src/main/java/com/ibm/engine/language/cxx/CxxDetectionEngine.java b/engine/src/main/java/com/ibm/engine/language/cxx/CxxDetectionEngine.java new file mode 100644 index 000000000..a95a6849b --- /dev/null +++ b/engine/src/main/java/com/ibm/engine/language/cxx/CxxDetectionEngine.java @@ -0,0 +1,173 @@ +/* + * 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.engine.language.cxx; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.detection.Handler; +import com.ibm.engine.detection.IDetectionEngine; +import com.ibm.engine.detection.MethodDetection; +import com.ibm.engine.detection.ResolvedValue; +import com.ibm.engine.detection.TraceSymbol; +import com.ibm.engine.language.cxx.antlr.CPP14ParserBaseListener; +import com.ibm.engine.model.factory.IValueFactory; +import com.ibm.engine.rule.Parameter; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.tree.ParseTreeWalker; + +public class CxxDetectionEngine implements IDetectionEngine { + private final DetectionStore + detectionStore; + private final Handler handler; + + public CxxDetectionEngine( + @Nonnull + DetectionStore + detectionStore, + @Nonnull Handler handler) { + this.detectionStore = detectionStore; + this.handler = handler; + } + + @Override + public void run(@Nonnull ParserRuleContext tree) { + run(TraceSymbol.createStart(), tree); + } + + @Override + public void run(@Nonnull TraceSymbol traceSymbol, @Nonnull ParserRuleContext tree) { + ParseTreeWalker walker = new ParseTreeWalker(); + walker.walk( + new CPP14ParserBaseListener() { + @Override + public void enterEveryRule(ParserRuleContext ctx) { + if (detectionStore + .getDetectionRule() + .match(ctx, handler.getLanguageSupport().translation())) { + detectionStore.onReceivingNewDetection( + new MethodDetection<>(ctx, null)); + } + } + }, + tree); + } + + @Nullable @Override + public ParserRuleContext extractArgumentFromMethodCaller( + @Nonnull ParserRuleContext methodDefinition, + @Nonnull ParserRuleContext methodInvocation, + @Nonnull ParserRuleContext methodParameterIdentifier) { + // In C++, methodDefinition might be a FunctionDefinitionContext + // and methodInvocation a PostfixExpressionContext (the call) + if (methodInvocation instanceof CPP14Parser.PostfixExpressionContext callCtx) { + // postfixExpression '(' expressionList? ')' + if (callCtx.getChildCount() >= 3 && callCtx.getChild(1).getText().equals("(")) { + ParseTree thirdChild = callCtx.getChild(2); + if (thirdChild instanceof CPP14Parser.ExpressionListContext argList) { + // This is a simplified version. We'd need to match the parameter index. + // For now, let's just return the whole list or a specific index if we knew it. + // But usually, the engine asks for a specific parameter by its identifier. + // In C++, we need to find the index of methodParameterIdentifier in + // methodDefinition. + + // TODO: Implement proper parameter index matching + } + } + } + return null; + } + + @Nonnull + @Override + public List> resolveValuesInInnerScope( + @Nonnull Class clazz, + @Nonnull ParserRuleContext expression, + @Nullable IValueFactory valueFactory) { + // Basic resolution: return the expression itself as a resolved value + // if it matches the expected type (best effort). + List> results = new LinkedList<>(); + // For now, just return the expression text if O is String + if (clazz.equals(String.class)) { + results.add(new ResolvedValue<>((O) expression.getText(), expression)); + } + return results; + } + + @Override + public void resolveValuesInOuterScope( + @Nonnull ParserRuleContext expression, + @Nonnull Parameter parameter) { + // TODO: Implement outer scope resolution (requires cross-file/cross-function tracking) + } + + @Override + public void resolveMethodReturnValues( + @Nonnull Class clazz, + @Nonnull ParserRuleContext methodDefinition, + @Nonnull Parameter parameter) { + // TODO: Implement + } + + @Nullable @Override + public ResolvedValue resolveEnumValue( + @Nonnull Class clazz, + @Nonnull ParserRuleContext enumClassDefinition, + @Nonnull LinkedList selections) { + return null; + } + + @Nonnull + @Override + public Optional> getAssignedSymbol( + @Nonnull ParserRuleContext expression) { + return Optional.empty(); + } + + @Nonnull + @Override + public Optional> getMethodInvocationParameterSymbol( + @Nonnull ParserRuleContext methodInvocation, + @Nonnull Parameter parameter) { + return Optional.empty(); + } + + @Nonnull + @Override + public Optional> getNewClassParameterSymbol( + @Nonnull ParserRuleContext newClass, @Nonnull Parameter parameter) { + return Optional.empty(); + } + + @Override + public boolean isInvocationOnVariable( + ParserRuleContext methodInvocation, @Nonnull TraceSymbol variableSymbol) { + return false; + } + + @Override + public boolean isInitForVariable( + ParserRuleContext newClass, @Nonnull TraceSymbol variableSymbol) { + return false; + } +} diff --git a/engine/src/main/java/com/ibm/engine/language/cxx/CxxLanguageSupport.java b/engine/src/main/java/com/ibm/engine/language/cxx/CxxLanguageSupport.java new file mode 100644 index 000000000..3acaedbbf --- /dev/null +++ b/engine/src/main/java/com/ibm/engine/language/cxx/CxxLanguageSupport.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.engine.language.cxx; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.detection.EnumMatcher; +import com.ibm.engine.detection.Handler; +import com.ibm.engine.detection.IBaseMethodVisitorFactory; +import com.ibm.engine.detection.IDetectionEngine; +import com.ibm.engine.detection.MatchContext; +import com.ibm.engine.detection.MethodMatcher; +import com.ibm.engine.executive.DetectionExecutive; +import com.ibm.engine.language.ILanguageSupport; +import com.ibm.engine.language.ILanguageTranslation; +import com.ibm.engine.language.IScanContext; +import com.ibm.engine.rule.IDetectionRule; +import java.util.Optional; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.antlr.v4.runtime.ParserRuleContext; + +public final class CxxLanguageSupport + implements ILanguageSupport { + + @Nonnull private final Handler handler; + + @Nonnull private final CxxLanguageTranslation translation; + + public CxxLanguageSupport() { + this.handler = new Handler<>(this); + this.translation = new CxxLanguageTranslation(); + } + + @Nonnull + @Override + public ILanguageTranslation translation() { + return translation; + } + + @Nonnull + @Override + public DetectionExecutive + createDetectionExecutive( + @Nonnull ParserRuleContext tree, + @Nonnull IDetectionRule detectionRule, + @Nonnull IScanContext scanContext) { + return new DetectionExecutive<>(tree, detectionRule, scanContext, this.handler); + } + + @Nonnull + @Override + public IDetectionEngine createDetectionEngineInstance( + @Nonnull + DetectionStore + detectionStore) { + return new CxxDetectionEngine(detectionStore, this.handler); + } + + @Nonnull + @Override + public IBaseMethodVisitorFactory getBaseMethodVisitorFactory() { + return CxxBaseMethodVisitor::new; + } + + @Nonnull + @Override + public Optional getEnclosingMethod(@Nonnull ParserRuleContext expression) { + return Optional.empty(); + } + + @Nullable @Override + public MethodMatcher createMethodMatcherBasedOn( + @Nonnull ParserRuleContext methodDefinition) { + return null; + } + + @Nullable @Override + public EnumMatcher createSimpleEnumMatcherFor( + @Nonnull ParserRuleContext enumIdentifier, @Nonnull MatchContext matchContext) { + return null; + } +} diff --git a/engine/src/main/java/com/ibm/engine/language/cxx/CxxLanguageTranslation.java b/engine/src/main/java/com/ibm/engine/language/cxx/CxxLanguageTranslation.java new file mode 100644 index 000000000..7baefe513 --- /dev/null +++ b/engine/src/main/java/com/ibm/engine/language/cxx/CxxLanguageTranslation.java @@ -0,0 +1,120 @@ +/* + * 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.engine.language.cxx; + +import com.ibm.engine.detection.IType; +import com.ibm.engine.detection.MatchContext; +import com.ibm.engine.language.ILanguageTranslation; +import com.ibm.engine.language.cxx.antlr.CPP14Parser; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import javax.annotation.Nonnull; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.tree.ParseTree; + +public class CxxLanguageTranslation implements ILanguageTranslation { + @Nonnull + @Override + public Optional getEnumIdentifierName( + @Nonnull MatchContext context, @Nonnull ParserRuleContext identifier) { + return Optional.empty(); + } + + @Nonnull + @Override + public Optional getMethodName( + @Nonnull MatchContext matchContext, @Nonnull ParserRuleContext methodInvocation) { + if (methodInvocation instanceof CPP14Parser.PostfixExpressionContext ctx) { + // Check for function call pattern: postfixExpression '(' expressionList? ')' + if (ctx.getChildCount() >= 3 && ctx.getChild(1).getText().equals("(")) { + ParseTree firstChild = ctx.getChild(0); + // Handle complex postfix expressions like obj.func or obj->func + if (firstChild instanceof CPP14Parser.PostfixExpressionContext firstPostfix) { + if (firstPostfix.getChildCount() >= 3) { + String op = firstPostfix.getChild(1).getText(); + if (op.equals(".") || op.equals("->")) { + return Optional.of(firstPostfix.getChild(2).getText()); + } + } + } + return Optional.of(firstChild.getText()); + } + } + return Optional.empty(); + } + + @Nonnull + @Override + public Optional getInvokedObjectTypeString( + @Nonnull MatchContext matchContext, @Nonnull ParserRuleContext methodInvocation) { + // For C++, object type might be hard to get without semantic analysis. + // For now, we can try to extract the qualifier if it's a member access. + if (methodInvocation instanceof CPP14Parser.PostfixExpressionContext ctx) { + if (ctx.getChildCount() >= 3 && ctx.getChild(1).getText().equals("(")) { + ParseTree firstChild = ctx.getChild(0); + if (firstChild instanceof CPP14Parser.PostfixExpressionContext firstPostfix) { + if (firstPostfix.getChildCount() >= 3) { + String op = firstPostfix.getChild(1).getText(); + if (op.equals(".") || op.equals("->")) { + String qualifier = firstPostfix.getChild(0).getText(); + return Optional.of((String type) -> type.equals(qualifier)); + } + } + } + } + } + return Optional.empty(); + } + + @Nonnull + @Override + public Optional getMethodReturnTypeString( + @Nonnull MatchContext matchContext, @Nonnull ParserRuleContext methodInvocation) { + return Optional.empty(); + } + + @Nonnull + @Override + public List getMethodParameterTypes( + @Nonnull MatchContext matchContext, @Nonnull ParserRuleContext methodInvocation) { + // In C++, we don't have easy access to types without a symbol table. + // We could potentially return the text of the parameters as a fallback for matching. + return Collections.emptyList(); + } + + @Nonnull + @Override + public Optional resolveIdentifierAsString( + @Nonnull MatchContext matchContext, @Nonnull ParserRuleContext identifierTree) { + if (identifierTree instanceof CPP14Parser.IdExpressionContext + || identifierTree instanceof CPP14Parser.UnqualifiedIdContext) { + return Optional.of(identifierTree.getText()); + } + return Optional.ofNullable(identifierTree.getText()); + } + + @Nonnull + @Override + public Optional getEnumClassName( + @Nonnull MatchContext matchContext, @Nonnull ParserRuleContext enumClass) { + return Optional.empty(); + } +} diff --git a/engine/src/main/java/com/ibm/engine/language/cxx/CxxParserErrorListener.java b/engine/src/main/java/com/ibm/engine/language/cxx/CxxParserErrorListener.java new file mode 100644 index 000000000..55ecce5a7 --- /dev/null +++ b/engine/src/main/java/com/ibm/engine/language/cxx/CxxParserErrorListener.java @@ -0,0 +1,49 @@ +/* + * 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.engine.language.cxx; + +import javax.annotation.Nonnull; +import org.antlr.v4.runtime.BaseErrorListener; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.fs.InputFile; + +public class CxxParserErrorListener extends BaseErrorListener { + + private static final Logger LOG = LoggerFactory.getLogger(CxxParserErrorListener.class); + private final InputFile inputFile; + + public CxxParserErrorListener(@Nonnull InputFile inputFile) { + this.inputFile = inputFile; + } + + @Override + public void syntaxError( + Recognizer recognizer, + Object offendingSymbol, + int line, + int charPositionInLine, + String msg, + RecognitionException e) { + LOG.debug("Syntax error in {}:{}:{} {}", inputFile, line, charPositionInLine, msg); + } +} diff --git a/engine/src/main/java/com/ibm/engine/language/cxx/CxxScanContext.java b/engine/src/main/java/com/ibm/engine/language/cxx/CxxScanContext.java new file mode 100644 index 000000000..f91185663 --- /dev/null +++ b/engine/src/main/java/com/ibm/engine/language/cxx/CxxScanContext.java @@ -0,0 +1,72 @@ +/* + * 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.engine.language.cxx; + +import com.ibm.engine.language.IScanContext; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.antlr.v4.runtime.ParserRuleContext; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.issue.NewIssue; +import org.sonar.api.batch.sensor.issue.NewIssueLocation; +import org.sonar.api.rule.RuleKey; +import org.sonar.check.Rule; + +/** C++ scan context wrapping the SonarQube SensorContext. */ +public record CxxScanContext( + @Nonnull SensorContext sensorContext, + @Nonnull InputFile inputFile, + @Nonnull String repositoryKey) + implements IScanContext { + + @Override + public void reportIssue( + @Nonnull CxxCheck currentRule, + @Nonnull ParserRuleContext tree, + @Nonnull String message) { + String ruleKey = getRuleKey(currentRule); + if (ruleKey == null) { + return; + } + int line = Math.max(1, tree.getStart().getLine()); + NewIssue issue = sensorContext.newIssue(); + NewIssueLocation location = + issue.newLocation().on(inputFile).at(inputFile.selectLine(line)).message(message); + issue.forRule(RuleKey.of(repositoryKey, ruleKey)).at(location).save(); + } + + @Nullable private static String getRuleKey(@Nonnull CxxCheck rule) { + Rule annotation = rule.getClass().getAnnotation(Rule.class); + return annotation != null ? annotation.key() : null; + } + + @Nonnull + @Override + public InputFile getInputFile() { + return inputFile; + } + + @Nonnull + @Override + public String getFilePath() { + return inputFile.uri().getPath(); + } +} diff --git a/engine/src/main/java/com/ibm/engine/language/cxx/CxxSymbol.java b/engine/src/main/java/com/ibm/engine/language/cxx/CxxSymbol.java new file mode 100644 index 000000000..744f8ae74 --- /dev/null +++ b/engine/src/main/java/com/ibm/engine/language/cxx/CxxSymbol.java @@ -0,0 +1,25 @@ +/* + * 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.engine.language.cxx; + +import javax.annotation.Nonnull; + +/** Representation of a C++ symbol (e.g. a variable name). */ +public record CxxSymbol(@Nonnull String name) {} diff --git a/engine/src/main/java/com/ibm/engine/language/cxx/antlr/CPP14ParserBase.java b/engine/src/main/java/com/ibm/engine/language/cxx/antlr/CPP14ParserBase.java new file mode 100644 index 000000000..12d96c65a --- /dev/null +++ b/engine/src/main/java/com/ibm/engine/language/cxx/antlr/CPP14ParserBase.java @@ -0,0 +1,41 @@ +/* + * 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.engine.language.cxx.antlr; + +import org.antlr.v4.runtime.*; + +public abstract class CPP14ParserBase extends Parser { + protected CPP14ParserBase(TokenStream input) { + super(input); + } + + protected boolean IsPureSpecifierAllowed() { + try { + var x = this._ctx; // memberDeclarator + var c = x.getChild(0).getChild(0); + var c2 = c.getChild(0); + var p = c2.getChild(1); + if (p == null) return false; + return (p instanceof CPP14Parser.ParametersAndQualifiersContext); + } catch (Exception e) { + } + return false; + } +} diff --git a/engine/src/main/java/com/ibm/engine/language/python/PythonDetectionEngine.java b/engine/src/main/java/com/ibm/engine/language/python/PythonDetectionEngine.java index c56513dba..7bd9dad09 100644 --- a/engine/src/main/java/com/ibm/engine/language/python/PythonDetectionEngine.java +++ b/engine/src/main/java/com/ibm/engine/language/python/PythonDetectionEngine.java @@ -77,6 +77,7 @@ public Tree extractArgumentFromMethodCaller( && methodParameterIdentifier instanceof Name nameTree) { // Check that we have the expected number of parameters + @Nonnull Optional> parameters = Optional.ofNullable(methodTree) .map(FunctionDef::parameters) @@ -110,11 +111,7 @@ public Tree extractArgumentFromMethodCaller( Optional name = parameters .filter(parameterList -> finalIndex < parameterList.size()) - .map( - parameterList -> - Optional.ofNullable(parameterList.get(finalIndex))) - // .filter(Objects::nonNull) - .map(Optional::get) + .map(parameterList -> parameterList.get(finalIndex)) .map(org.sonar.plugins.python.api.tree.Parameter::name) .map(Name::name); @@ -126,8 +123,9 @@ public Tree extractArgumentFromMethodCaller( return null; } + @Nonnull @Override - public @Nonnull List> resolveValuesInInnerScope( + public List> resolveValuesInInnerScope( @Nonnull Class clazz, @Nonnull Tree expression, @Nullable IValueFactory valueFactory) { @@ -154,13 +152,14 @@ public void resolveValuesInOuterScope( if (optionalMethodTree.isEmpty()) { return; } - Tree methodTree = optionalMethodTree.get(); + @Nonnull Tree methodTree = optionalMethodTree.get(); // If we cannot resolve the expression, it shoud be because it is an argument of the // enclosing function. We therefore create a hook, but we need to get the argument to // which `expressionTree` resolves to. // To do so, we call `resolveValues` with the special parameter // `returningEnclosingParam` set to true. + @Nonnull List> resolvedValues = PythonSemantic.resolveValues( Object.class, expressionTree, new LinkedList<>(), null, true, this); @@ -168,7 +167,7 @@ public void resolveValuesInOuterScope( if (resolvedValues.size() != 1) { return; } - final Tree resolvedParameter = resolvedValues.get(0).tree(); + @Nonnull final Tree resolvedParameter = resolvedValues.get(0).tree(); createAMethodHook(methodTree, resolvedParameter, detectableParameter); // Note that compared to the Java implementation, there is no case where we call @@ -227,16 +226,19 @@ public void resolveMethodReturnValues( throw new UnsupportedOperationException("Unimplemented method 'resolveMethodReturnValues'"); } - @Override + @Nullable @Override public ResolvedValue resolveEnumValue( - Class clazz, Tree enumClassDefinition, LinkedList selections) { + @Nonnull Class clazz, + @Nonnull Tree enumClassDefinition, + @Nonnull LinkedList selections) { // TODO: Enums are not a major part of Pythonm, it is left for later // https://docs.python.org/3/library/enum.html throw new UnsupportedOperationException("Unimplemented method 'resolveEnumValue'"); } + @Nonnull @Override - public Optional> getAssignedSymbol(Tree expression) { + public Optional> getAssignedSymbol(@Nonnull Tree expression) { // When the expression is an assignment like `43` in `global_var = 43`, it will return the // symbol of the Name `global_var`. // In Java, `getAssignedSymbol` seem to return the symbol of the *parent* of the expression. @@ -274,20 +276,24 @@ public Optional> getAssignedSymbol(Tree expression) { throw new UnsupportedOperationException("Unimplemented case."); } + @Nonnull @Override public Optional> getMethodInvocationParameterSymbol( - Tree methodInvocationTree, Parameter parameter) { + @Nonnull Tree methodInvocationTree, @Nonnull Parameter parameter) { if (methodInvocationTree instanceof CallExpression callExpression) { - return getTraceSymbol(parameter, callExpression.arguments()); + @Nonnull List arguments = callExpression.arguments(); + return getTraceSymbol(parameter, arguments); } return Optional.empty(); } + @Nonnull @Override public Optional> getNewClassParameterSymbol( - Tree newClass, Parameter parameter) { + @Nonnull Tree newClass, @Nonnull Parameter parameter) { if (newClass instanceof CallExpression callExpression) { - return getTraceSymbol(parameter, callExpression.arguments()); + @Nonnull List arguments = callExpression.arguments(); + return getTraceSymbol(parameter, arguments); } return Optional.empty(); } @@ -296,45 +302,52 @@ public Optional> getNewClassParameterSymbol( private Optional> getTraceSymbol( @Nonnull Parameter parameter, @Nonnull List arguments) { if (parameter.getIndex() >= arguments.size()) { - return Optional.of(TraceSymbol.createWithStateDifferent()); + @Nonnull + Optional> res = Optional.of(TraceSymbol.createWithStateDifferent()); + return res; } Argument arg = arguments.get(parameter.getIndex()); if (arg instanceof RegularArgument regularArg) { Expression expressionArg = regularArg.expression(); if (expressionArg.is(Tree.Kind.NAME)) { Name nameArg = (Name) expressionArg; - return Optional.of(TraceSymbol.createFrom(nameArg.symbol())); + @Nonnull + Optional> res = + Optional.of(TraceSymbol.createFrom(nameArg.symbol())); + return res; } } - return Optional.of(TraceSymbol.createWithStateNoSymbol()); + @Nonnull + Optional> res = Optional.of(TraceSymbol.createWithStateNoSymbol()); + return res; } @Override public boolean isInvocationOnVariable( - Tree methodInvocation, TraceSymbol variableSymbol) { + @Nonnull Tree methodInvocation, @Nonnull TraceSymbol variableSymbol) { if (methodInvocation instanceof CallExpression callExpression) { if (!variableSymbol.is(TraceSymbol.State.SYMBOL)) { return false; } Symbol variable = variableSymbol.getSymbol(); - Expression callee = callExpression.callee(); - if (variable == null || !callee.is(Tree.Kind.QUALIFIED_EXPR)) { + if (variable == null) { return false; } - QualifiedExpression qualifiedExpression = (QualifiedExpression) callee; - if (qualifiedExpression.qualifier() instanceof Name name) { - Optional nameString = Optional.of(name).map(Name::symbol).map(Symbol::name); - return nameString.isPresent() && nameString.get().equals(variable.name()); + if (callExpression.callee() instanceof QualifiedExpression qualifiedExpression) { + Expression qualifier = qualifiedExpression.qualifier(); + if (qualifier instanceof Name name) { + Symbol qualifierSymbol = name.symbol(); + return variable.equals(qualifierSymbol); + } } - - return false; } return false; } @Override - public boolean isInitForVariable(Tree newClass, TraceSymbol variableSymbol) { + public boolean isInitForVariable( + @Nonnull Tree newClass, @Nonnull TraceSymbol variableSymbol) { if (!variableSymbol.is(TraceSymbol.State.SYMBOL)) { return false; } @@ -351,6 +364,20 @@ public boolean isInitForVariable(Tree newClass, TraceSymbol variableSymb private void analyseExpression( @Nonnull TraceSymbol traceSymbol, @Nonnull CallExpression expressionTree) { + boolean isInvocation = + isInvocationOnVariable(expressionTree, traceSymbol) + || isInitForVariable(expressionTree, traceSymbol); + + // Check if the variable symbols for the method (if applicable) are connected + Optional assignedSymbol = + getAssignedSymbol(expressionTree).map(TraceSymbol::getSymbol); + + if (traceSymbol.is(TraceSymbol.State.DIFFERENT) + || (traceSymbol.is(TraceSymbol.State.SYMBOL) && !isInvocation) + || (traceSymbol.is(TraceSymbol.State.NO_SYMBOL) && assignedSymbol.isPresent())) { + return; + } + if (detectionStore.getDetectionRule().is(MethodDetectionRule.class)) { MethodDetection methodDetection = new MethodDetection<>(expressionTree, null); detectionStore.onReceivingNewDetection(methodDetection); @@ -364,22 +391,16 @@ private void analyseExpression( } // Extracts the arguments for the provided expression - List arguments = expressionTree.arguments(); - boolean isInvocation = - isInvocationOnVariable(expressionTree, traceSymbol) - || isInitForVariable(expressionTree, traceSymbol); - // TODO: It would be better to have a case disjunction to use either - // isInvocationOnVariable or isInitForVariable, but it is difficult in Python + @Nonnull List arguments = expressionTree.arguments(); int index = 0; for (Parameter parameter : detectionRule.parameters()) { - if (!checkCurrentIndexState( - index, arguments, isInvocation, traceSymbol, expressionTree)) { + if (arguments.size() <= index) { index++; continue; } // the expression tree of the parameter - Tree expression = arguments.get(index); // this is an Argument tree + @Nonnull Tree expression = arguments.get(index); // this is an Argument tree if (expression instanceof RegularArgument regularArgument) { expression = regularArgument.expression(); } @@ -392,6 +413,7 @@ private void analyseExpression( DetectableParameter detectableParameter = (DetectableParameter) parameter; // try to resolve value in inner scope + @Nonnull List> resolvedValues = resolveValuesInInnerScope( Object.class, expression, detectableParameter.getiValueFactory()); @@ -401,12 +423,14 @@ private void analyseExpression( } else { resolvedValues.stream() .map( - resolvedValue -> - new ValueDetection<>( - resolvedValue, - detectableParameter, - expressionTree, - expressionTree)) + resolvedValue -> { + @Nonnull ResolvedValue val = resolvedValue; + return new ValueDetection<>( + val, + detectableParameter, + expressionTree, + expressionTree); + }) .forEach(detectionStore::onReceivingNewDetection); } } else if (!parameter.getDetectionRules().isEmpty()) { @@ -417,40 +441,12 @@ private void analyseExpression( * In this case, we resolve the parameter with the depending detection rule with an EXPRESSION scope, * this way we ensure to only resolve the right parameter content and not similar calls in the same function scope. */ + @Nonnull Tree expr = expression; detectionStore.onDetectedDependingParameter( - parameter, expression, DetectionStore.Scope.EXPRESSION); + parameter, expr, DetectionStore.Scope.EXPRESSION); } index++; } } - - private boolean checkCurrentIndexState( - int index, - List arguments, - boolean isInvocation, - @Nonnull TraceSymbol traceSymbol, - @Nonnull CallExpression expressionTree) { - /* - * Check if the matched method does have equal or less number of arguments compared to the index - * of interested defined in the detection rule. - * This will prevent an index out of bound - */ - if (arguments.size() <= index) { - return false; - } - - // Check if the variable symbols for the method (if applicable) are connected - Optional assignedSymbol = - getAssignedSymbol(expressionTree).map(ts -> ts.getSymbol()); - - return !(traceSymbol.is(TraceSymbol.State.DIFFERENT) - || - // checks if a symbol is set and therefore expected, then check if the symbols - // match. - (traceSymbol.is(TraceSymbol.State.SYMBOL) && !isInvocation) - || - // checks if no symbol is expected, but the matched method has one. - (traceSymbol.is(TraceSymbol.State.NO_SYMBOL) && assignedSymbol.isPresent())); - } } diff --git a/engine/src/main/java/com/ibm/engine/language/python/PythonLanguageTranslation.java b/engine/src/main/java/com/ibm/engine/language/python/PythonLanguageTranslation.java index e644f4360..4c863be9c 100644 --- a/engine/src/main/java/com/ibm/engine/language/python/PythonLanguageTranslation.java +++ b/engine/src/main/java/com/ibm/engine/language/python/PythonLanguageTranslation.java @@ -68,7 +68,7 @@ public Optional getInvokedObjectTypeString( // well as the "invoked object type" (`cryptography.hazmat.primitives.asymmetric.dsa`) // used in the rule's `forObjectTypes` if (callExpression.callee() instanceof QualifiedExpression qualifiedExpression) { - return PythonSemantic.resolveTreeType(qualifiedExpression.name()); + return PythonSemantic.resolveTreeType(qualifiedExpression.qualifier()); } else if (callExpression.callee() instanceof Name functionName) { return PythonSemantic.resolveTreeType(functionName); } diff --git a/engine/src/main/java/com/ibm/engine/language/python/PythonSemantic.java b/engine/src/main/java/com/ibm/engine/language/python/PythonSemantic.java index 49d85e209..ed2b165f0 100644 --- a/engine/src/main/java/com/ibm/engine/language/python/PythonSemantic.java +++ b/engine/src/main/java/com/ibm/engine/language/python/PythonSemantic.java @@ -131,8 +131,8 @@ public static Optional resolveTreeType(@Nonnull Tree tree) { Objects.equals(nameClassSymbol.fullyQualifiedName(), string)); } } - // Otherwise, we accept all types - return Optional.of((String string) -> true); + // Otherwise, we do not accept any type + return Optional.of((String string) -> false); } // Obtain the type from the content diff --git a/pom.xml b/pom.xml index a58b134bf..dcf158c44 100644 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,7 @@ java python go + cxx engine output common diff --git a/sonar-cryptography-plugin/pom.xml b/sonar-cryptography-plugin/pom.xml index 189ab59a2..a94e12b0a 100644 --- a/sonar-cryptography-plugin/pom.xml +++ b/sonar-cryptography-plugin/pom.xml @@ -41,6 +41,12 @@ 2.0.0-SNAPSHOT compile + + com.ibm + cxx + 2.0.0-SNAPSHOT + compile + @@ -65,7 +71,7 @@ Sonar Crypto Plugin com.ibm.plugin.CryptographyPlugin - java,jsp,py,ipynb,go + java,jsp,py,ipynb,go,cpp,c,cxx,cc,h,hpp ${sonar.minVersion} true true diff --git a/sonar-cryptography-plugin/src/main/java/com/ibm/plugin/CryptographyPlugin.java b/sonar-cryptography-plugin/src/main/java/com/ibm/plugin/CryptographyPlugin.java index 59bb441e6..0052edfd4 100644 --- a/sonar-cryptography-plugin/src/main/java/com/ibm/plugin/CryptographyPlugin.java +++ b/sonar-cryptography-plugin/src/main/java/com/ibm/plugin/CryptographyPlugin.java @@ -48,6 +48,9 @@ public void define(Context context) { // golang GoScannerRuleDefinition.class, // Define Go rules CryptoGoSensor.class, // Custom sensor (sonar-go has no CheckRegistrar API) + // c++ + CxxScannerRuleDefinition.class, // Define C++ rules + CryptoCxxSensor.class, // Custom sensor (sonar-cxx has no CheckRegistrar API) // general OutputFileJob.class); } diff --git a/sonar-cryptography-plugin/src/main/java/com/ibm/plugin/ScannerManager.java b/sonar-cryptography-plugin/src/main/java/com/ibm/plugin/ScannerManager.java index 5570021ef..bf0824ea4 100644 --- a/sonar-cryptography-plugin/src/main/java/com/ibm/plugin/ScannerManager.java +++ b/sonar-cryptography-plugin/src/main/java/com/ibm/plugin/ScannerManager.java @@ -66,6 +66,7 @@ private List getAggregatedNodes() { nodes.addAll(JavaAggregator.getDetectedNodes()); nodes.addAll(PythonAggregator.getDetectedNodes()); nodes.addAll(GoAggregator.getDetectedNodes()); + nodes.addAll(CxxAggregator.getDetectedNodes()); return nodes; } @@ -73,5 +74,6 @@ public void reset() { JavaAggregator.reset(); PythonAggregator.reset(); GoAggregator.reset(); + CxxAggregator.reset(); } }