From 011fe44c3822f6449f47a30827328b3d09255645 Mon Sep 17 00:00:00 2001 From: Divyateja Indrakanti Date: Wed, 6 May 2026 07:40:48 +0530 Subject: [PATCH 01/17] Create pom.xml Signed-off-by: Divyateja Indrakanti --- cpp/pom.xml | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 cpp/pom.xml diff --git a/cpp/pom.xml b/cpp/pom.xml new file mode 100644 index 00000000..b16687f8 --- /dev/null +++ b/cpp/pom.xml @@ -0,0 +1,149 @@ + + + + 4.0.0 + + + com.ibm + sonar-cryptography + 1.5.1-SNAPSHOT + ../pom.xml + + + sonar-cryptography-cpp + Sonar Cryptography Plugin :: C/C++ + + C and C++ language support for the Sonar Cryptography Plugin, + with detection rules for OpenSSL and other cryptographic libraries. + + + + 17 + 17 + + + + + + + com.ibm + sonar-cryptography-common + ${project.version} + + + + com.ibm + sonar-cryptography-engine + ${project.version} + + + + com.ibm + sonar-cryptography-mapper + ${project.version} + + + + com.ibm + sonar-cryptography-enricher + ${project.version} + + + + com.ibm + sonar-cryptography-output + ${project.version} + + + + + org.sonarsource.api.plugin + sonar-plugin-api + provided + + + + + org.antlr + antlr4-runtime + + + + + com.google.code.findbugs + jsr305 + + + + + org.slf4j + slf4j-api + + + + + org.junit.jupiter + junit-jupiter + test + + + + org.assertj + assertj-core + test + + + + org.mockito + mockito-core + test + + + + org.sonarsource.api.plugin + sonar-plugin-api-test-fixtures + test + + + + + + + + + + com.diffplug.spotless + spotless-maven-plugin + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + + + From 76780967d948dc6cae6425f37301eb623a83c19c Mon Sep 17 00:00:00 2001 From: Divyateja Indrakanti Date: Wed, 6 May 2026 07:53:52 +0530 Subject: [PATCH 02/17] Update pom.xml Signed-off-by: Divyateja Indrakanti --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index a32d3c77..f411054a 100644 --- a/pom.xml +++ b/pom.xml @@ -14,6 +14,7 @@ python go csharp + cpp engine output common From ff13a05e8075ec02cb46bde7ad2c49ef86011a6d Mon Sep 17 00:00:00 2001 From: Divyateja Indrakanti Date: Wed, 6 May 2026 07:54:43 +0530 Subject: [PATCH 03/17] Update pom.xml Signed-off-by: Divyateja Indrakanti --- sonar-cryptography-plugin/pom.xml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sonar-cryptography-plugin/pom.xml b/sonar-cryptography-plugin/pom.xml index 9ac6bfe6..a181f9ee 100644 --- a/sonar-cryptography-plugin/pom.xml +++ b/sonar-cryptography-plugin/pom.xml @@ -47,6 +47,12 @@ 2.0.0-SNAPSHOT compile + + com.ibm + cpp + 2.0.0-SNAPSHOT + compile + @@ -71,7 +77,7 @@ Sonar Crypto Plugin com.ibm.plugin.CryptographyPlugin - java,jsp,py,ipynb,go,cs + java,jsp,py,ipynb,go,cs,c,cpp,h ${sonar.minVersion} true true @@ -104,7 +110,7 @@ NOTICE* - + @@ -174,4 +180,4 @@ - \ No newline at end of file + From 3b81e597043970b5d67cffadd05dccde822ab674 Mon Sep 17 00:00:00 2001 From: Divyateja Indrakanti Date: Wed, 6 May 2026 08:40:36 +0530 Subject: [PATCH 04/17] Create CLexer.g4 Signed-off-by: Divyateja Indrakanti --- .../engine/language/csharp/antlr/CLexer.g4 | 359 ++++++++++++++++++ 1 file changed, 359 insertions(+) create mode 100644 engine/src/main/antlr4/com/ibm/engine/language/csharp/antlr/CLexer.g4 diff --git a/engine/src/main/antlr4/com/ibm/engine/language/csharp/antlr/CLexer.g4 b/engine/src/main/antlr4/com/ibm/engine/language/csharp/antlr/CLexer.g4 new file mode 100644 index 00000000..581c03ca --- /dev/null +++ b/engine/src/main/antlr4/com/ibm/engine/language/csharp/antlr/CLexer.g4 @@ -0,0 +1,359 @@ +/* + * 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. + */ + +/* + * C Lexer Grammar for CBOMkit sonar-cryptography plugin. + * Adapted from the ANTLR4 grammars-v4 C grammar: + * https://github.com/antlr/grammars-v4/tree/master/c + * + * Supports C89, C99, C11 constructs sufficient for detecting + * OpenSSL and other cryptographic library API calls. + */ +lexer grammar CLexer; + +// --------------------------------------------------------------------------- +// Keywords +// --------------------------------------------------------------------------- + +Auto : 'auto'; +Break : 'break'; +Case : 'case'; +Char : 'char'; +Const : 'const'; +Continue : 'continue'; +Default : 'default'; +Do : 'do'; +Double : 'double'; +Else : 'else'; +Enum : 'enum'; +Extern : 'extern'; +Float : 'float'; +For : 'for'; +Goto : 'goto'; +If : 'if'; +Inline : 'inline'; +Int : 'int'; +Long : 'long'; +Register : 'register'; +Restrict : 'restrict'; +Return : 'return'; +Short : 'short'; +Signed : 'signed'; +Sizeof : 'sizeof'; +Static : 'static'; +Struct : 'struct'; +Switch : 'switch'; +Typedef : 'typedef'; +Union : 'union'; +Unsigned : 'unsigned'; +Void : 'void'; +Volatile : 'volatile'; +While : 'while'; + +// C11 keywords +Alignas : '_Alignas'; +Alignof : '_Alignof'; +Atomic : '_Atomic'; +Bool : '_Bool'; +Complex : '_Complex'; +Generic : '_Generic'; +Imaginary : '_Imaginary'; +Noreturn : '_Noreturn'; +StaticAssert : '_Static_assert'; +ThreadLocal : '_Thread_local'; + +// GCC extensions (common in OpenSSL code) +BuiltinVaArg : '__builtin_va_arg'; +BuiltinOffsetof : '__builtin_offsetof'; + +// --------------------------------------------------------------------------- +// Punctuators and operators +// --------------------------------------------------------------------------- + +LeftParen : '('; +RightParen : ')'; +LeftBracket : '['; +RightBracket : ']'; +LeftBrace : '{'; +RightBrace : '}'; + +Less : '<'; +LessEqual : '<='; +Greater : '>'; +GreaterEqual : '>='; +LeftShift : '<<'; +RightShift : '>>'; + +Plus : '+'; +PlusPlus : '++'; +Minus : '-'; +MinusMinus : '--'; +Star : '*'; +Div : '/'; +Mod : '%'; + +And : '&'; +Or : '|'; +AndAnd : '&&'; +OrOr : '||'; +Caret : '^'; +Not : '!'; +Tilde : '~'; + +Question : '?'; +Colon : ':'; +Semi : ';'; +Comma : ','; +Assign : '='; + +// Compound assignment operators +StarAssign : '*='; +DivAssign : '/='; +ModAssign : '%='; +PlusAssign : '+='; +MinusAssign : '-='; +LeftShiftAssign : '<<='; +RightShiftAssign: '>>='; +AndAssign : '&='; +XorAssign : '^='; +OrAssign : '|='; + +Equal : '=='; +NotEqual : '!='; + +Arrow : '->'; +Dot : '.'; +Ellipsis : '...'; + +// --------------------------------------------------------------------------- +// Literals +// --------------------------------------------------------------------------- + +IntegerConstant + : DecimalConstant IntegerSuffix? + | OctalConstant IntegerSuffix? + | HexadecimalConstant IntegerSuffix? + | BinaryConstant + ; + +fragment BinaryConstant + : '0' [bB] [0-1]+ + ; + +fragment DecimalConstant + : NonzeroDigit Digit* + ; + +fragment OctalConstant + : '0' OctalDigit* + ; + +fragment HexadecimalConstant + : HexadecimalPrefix HexadecimalDigit+ + ; + +fragment HexadecimalPrefix + : '0' [xX] + ; + +fragment IntegerSuffix + : UnsignedSuffix LongSuffix? + | UnsignedSuffix LongLongSuffix + | LongSuffix UnsignedSuffix? + | LongLongSuffix UnsignedSuffix? + ; + +fragment UnsignedSuffix + : [uU] + ; + +fragment LongSuffix + : [lL] + ; + +fragment LongLongSuffix + : 'll' | 'LL' + ; + +FloatingConstant + : DecimalFloatingConstant + | HexadecimalFloatingConstant + ; + +fragment DecimalFloatingConstant + : FractionalConstant ExponentPart? FloatingSuffix? + | DigitSequence ExponentPart FloatingSuffix? + ; + +fragment HexadecimalFloatingConstant + : HexadecimalPrefix (HexadecimalFractionalConstant | HexadecimalDigitSequence) BinaryExponentPart FloatingSuffix? + ; + +fragment FractionalConstant + : DigitSequence? '.' DigitSequence + | DigitSequence '.' + ; + +fragment ExponentPart + : [eE] Sign? DigitSequence + ; + +fragment Sign + : [+-] + ; + +fragment DigitSequence + : Digit+ + ; + +fragment HexadecimalFractionalConstant + : HexadecimalDigitSequence? '.' HexadecimalDigitSequence + | HexadecimalDigitSequence '.' + ; + +fragment BinaryExponentPart + : [pP] Sign? DigitSequence + ; + +fragment HexadecimalDigitSequence + : HexadecimalDigit+ + ; + +fragment FloatingSuffix + : [flFL] + ; + +CharacterConstant + : '\'' CCharSequence '\'' + | 'L\'' CCharSequence '\'' + | 'u\'' CCharSequence '\'' + | 'U\'' CCharSequence '\'' + ; + +fragment CCharSequence + : CChar+ + ; + +fragment CChar + : ~['\\\r\n] + | EscapeSequence + ; + +StringLiteral + : EncodingPrefix? '"' SCharSequence? '"' + ; + +fragment EncodingPrefix + : 'u8' | 'u' | 'U' | 'L' + ; + +fragment SCharSequence + : SChar+ + ; + +fragment SChar + : ~["\\\r\n] + | EscapeSequence + | '\\\n' // Added line + | '\\\r\n' // Added line + ; + +fragment EscapeSequence + : SimpleEscapeSequence + | OctalEscapeSequence + | HexadecimalEscapeSequence + | UniversalCharacterName + ; + +fragment SimpleEscapeSequence + : '\\' ['"?abfnrtvv\\] + ; + +fragment OctalEscapeSequence + : '\\' OctalDigit OctalDigit? OctalDigit? + ; + +fragment HexadecimalEscapeSequence + : '\\x' HexadecimalDigit+ + ; + +// --------------------------------------------------------------------------- +// Identifiers +// --------------------------------------------------------------------------- + +Identifier + : IdentifierNondigit (IdentifierNondigit | Digit)* + ; + +fragment IdentifierNondigit + : Nondigit + | UniversalCharacterName + ; + +fragment Nondigit + : [a-zA-Z_] + ; + +fragment Digit + : [0-9] + ; + +fragment NonzeroDigit + : [1-9] + ; + +fragment OctalDigit + : [0-7] + ; + +fragment HexadecimalDigit + : [0-9a-fA-F] + ; + +fragment UniversalCharacterName + : '\\u' HexadecimalDigit HexadecimalDigit HexadecimalDigit HexadecimalDigit + | '\\U' HexadecimalDigit HexadecimalDigit HexadecimalDigit HexadecimalDigit + HexadecimalDigit HexadecimalDigit HexadecimalDigit HexadecimalDigit + ; + +// --------------------------------------------------------------------------- +// Preprocessor directives (skip — we do not expand macros) +// --------------------------------------------------------------------------- + +Directive + : '#' ~[\r\n]* -> skip + ; + +// --------------------------------------------------------------------------- +// Whitespace and comments +// --------------------------------------------------------------------------- + +Whitespace + : [ \t]+ -> skip + ; + +Newline + : ( '\r' '\n'? | '\n' ) -> skip + ; + +BlockComment + : '/*' .*? '*/' -> skip + ; + +LineComment + : '//' ~[\r\n]* -> skip + ; From 49b90cf0314dc24d5d2dd47607435f7d7e5aabb7 Mon Sep 17 00:00:00 2001 From: Divyateja Indrakanti Date: Wed, 6 May 2026 08:42:01 +0530 Subject: [PATCH 05/17] Create CParser.g4 Signed-off-by: Divyateja Indrakanti --- .../engine/language/csharp/antlr/CParser.g4 | 494 ++++++++++++++++++ 1 file changed, 494 insertions(+) create mode 100644 engine/src/main/antlr4/com/ibm/engine/language/csharp/antlr/CParser.g4 diff --git a/engine/src/main/antlr4/com/ibm/engine/language/csharp/antlr/CParser.g4 b/engine/src/main/antlr4/com/ibm/engine/language/csharp/antlr/CParser.g4 new file mode 100644 index 00000000..41866acf --- /dev/null +++ b/engine/src/main/antlr4/com/ibm/engine/language/csharp/antlr/CParser.g4 @@ -0,0 +1,494 @@ +/* + * 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. + */ + +/* + * C Parser Grammar for CBOMkit sonar-cryptography plugin. + * Adapted from the ANTLR4 grammars-v4 C grammar: + * https://github.com/antlr/grammars-v4/tree/master/c + * + * Supports C89, C99, C11 constructs sufficient for detecting + * OpenSSL and other cryptographic library API calls. + * + * Key design goals: + * - Detect function calls: EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), ...) + * - Detect string literals used as algorithm identifiers + * - Detect integer constants used as key sizes + * - Handle pointer dereferences and struct member access + */ +parser grammar CParser; + +options { + tokenVocab = CLexer; +} + +// --------------------------------------------------------------------------- +// Top-level rule +// --------------------------------------------------------------------------- + +compilationUnit + : translationUnit? EOF + ; + +translationUnit + : externalDeclaration+ + ; + +externalDeclaration + : functionDefinition + | declaration + | Semi // stray semicolons + ; + +// --------------------------------------------------------------------------- +// Function definitions +// --------------------------------------------------------------------------- + +functionDefinition + : declarationSpecifiers declarator declarationList? compoundStatement + ; + +declarationList + : declaration+ + ; + +// --------------------------------------------------------------------------- +// Declarations +// --------------------------------------------------------------------------- + +declaration + : declarationSpecifiers initDeclaratorList? Semi + | staticAssertDeclaration + ; + +declarationSpecifiers + : declarationSpecifier+ + ; + +declarationSpecifiers2 + : declarationSpecifier+ + ; + +declarationSpecifier + : storageClassSpecifier + | typeSpecifier + | typeQualifier + | functionSpecifier + | alignmentSpecifier + ; + +initDeclaratorList + : initDeclarator ( Comma initDeclarator )* + ; + +initDeclarator + : declarator ( Assign initializer )? + ; + +storageClassSpecifier + : Typedef + | Extern + | Static + | ThreadLocal + | Auto + | Register + ; + +typeSpecifier + : Void + | Char + | Short + | Int + | Long + | Float + | Double + | Signed + | Unsigned + | Bool + | Complex + | atomicTypeSpecifier + | structOrUnionSpecifier + | enumSpecifier + | typedefName + | typeofSpecifier + ; + +// typeof() — GCC extension common in OpenSSL +typeofSpecifier + : '__typeof__' LeftParen (expression | typeName) RightParen + | '__typeof' LeftParen (expression | typeName) RightParen + ; + +structOrUnionSpecifier + : structOrUnion Identifier? LeftBrace structDeclarationList RightBrace + | structOrUnion Identifier + ; + +structOrUnion + : 'struct' + | 'union' + ; + +structDeclarationList + : structDeclaration+ + ; + +structDeclaration + : specifierQualifierList structDeclaratorList? Semi + | staticAssertDeclaration + ; + +specifierQualifierList + : (typeSpecifier | typeQualifier | alignmentSpecifier)+ + ; + +structDeclaratorList + : structDeclarator (Comma structDeclarator)* + ; + +structDeclarator + : declarator + | declarator? Colon constantExpression + ; + +enumSpecifier + : 'enum' Identifier? LeftBrace enumeratorList Comma? RightBrace + | 'enum' Identifier + ; + +enumeratorList + : enumerator (Comma enumerator)* + ; + +enumerator + : enumerationConstant (Assign constantExpression)? + ; + +enumerationConstant + : Identifier + ; + +atomicTypeSpecifier + : Atomic LeftParen typeName RightParen + ; + +typeQualifier + : Const + | Restrict + | Volatile + | Atomic + ; + +functionSpecifier + : Inline + | Noreturn + ; + +alignmentSpecifier + : Alignas LeftParen (typeName | constantExpression) RightParen + ; + +declarator + : pointer? directDeclarator + ; + +directDeclarator + : Identifier + | LeftParen declarator RightParen + | directDeclarator LeftBracket typeQualifierList? assignmentExpression? RightBracket + | directDeclarator LeftBracket Static typeQualifierList? assignmentExpression RightBracket + | directDeclarator LeftBracket typeQualifierList Static assignmentExpression RightBracket + | directDeclarator LeftBracket typeQualifierList? Star RightBracket + | directDeclarator LeftParen parameterTypeList RightParen + | directDeclarator LeftParen identifierList? RightParen + ; + +pointer + : ( Star typeQualifierList? )+ + ; + +typeQualifierList + : typeQualifier+ + ; + +parameterTypeList + : parameterList ( Comma Ellipsis )? + ; + +parameterList + : parameterDeclaration ( Comma parameterDeclaration )* + ; + +parameterDeclaration + : declarationSpecifiers declarator + | declarationSpecifiers2 abstractDeclarator? + ; + +identifierList + : Identifier ( Comma Identifier )* + ; + +typeName + : specifierQualifierList abstractDeclarator? + ; + +abstractDeclarator + : pointer + | pointer? directAbstractDeclarator + ; + +directAbstractDeclarator + : LeftParen abstractDeclarator RightParen + | LeftBracket typeQualifierList? assignmentExpression? RightBracket + | LeftBracket Static typeQualifierList? assignmentExpression RightBracket + | LeftBracket typeQualifierList Static assignmentExpression RightBracket + | LeftBracket Star RightBracket + | LeftParen parameterTypeList? RightParen + | directAbstractDeclarator LeftBracket typeQualifierList? assignmentExpression? RightBracket + | directAbstractDeclarator LeftBracket Static typeQualifierList? assignmentExpression RightBracket + | directAbstractDeclarator LeftBracket typeQualifierList Static assignmentExpression RightBracket + | directAbstractDeclarator LeftBracket Star RightBracket + | directAbstractDeclarator LeftParen parameterTypeList? RightParen + ; + +typedefName + : Identifier + ; + +initializer + : assignmentExpression + | LeftBrace initializerList Comma? RightBrace + ; + +initializerList + : designation? initializer ( Comma designation? initializer )* + ; + +designation + : designatorList Assign + ; + +designatorList + : designator+ + ; + +designator + : LeftBracket constantExpression RightBracket + | Dot Identifier + ; + +staticAssertDeclaration + : StaticAssert LeftParen constantExpression Comma StringLiteral+ RightParen Semi + ; + +// --------------------------------------------------------------------------- +// Statements +// --------------------------------------------------------------------------- + +statement + : labeledStatement + | compoundStatement + | expressionStatement + | selectionStatement + | iterationStatement + | jumpStatement + ; + +labeledStatement + : Identifier Colon statement + | Case constantExpression Colon statement + | Default Colon statement + ; + +compoundStatement + : LeftBrace blockItemList? RightBrace + ; + +blockItemList + : blockItem+ + ; + +blockItem + : statement + | declaration + ; + +expressionStatement + : expression? Semi + ; + +selectionStatement + : If LeftParen expression RightParen statement ( Else statement )? + | Switch LeftParen expression RightParen statement + ; + +iterationStatement + : While LeftParen expression RightParen statement + | Do statement While LeftParen expression RightParen Semi + | For LeftParen forCondition RightParen statement + ; + +forCondition + : ( forDeclaration | expression? ) Semi forExpression? Semi forExpression? + ; + +forDeclaration + : declarationSpecifiers initDeclaratorList? + ; + +forExpression + : assignmentExpression ( Comma assignmentExpression )* + ; + +jumpStatement + : Goto Identifier Semi + | Continue Semi + | Break Semi + | Return expression? Semi + ; + +// --------------------------------------------------------------------------- +// Expressions +// --------------------------------------------------------------------------- + +compilationUnit2 + : expression EOF + ; + +expression + : assignmentExpression ( Comma assignmentExpression )* + ; + +assignmentExpression + : conditionalExpression + | unaryExpression assignmentOperator assignmentExpression + ; + +assignmentOperator + : Assign | StarAssign | DivAssign | ModAssign | PlusAssign | MinusAssign + | LeftShiftAssign | RightShiftAssign | AndAssign | XorAssign | OrAssign + ; + +conditionalExpression + : logicalOrExpression ( Question expression Colon conditionalExpression )? + ; + +logicalOrExpression + : logicalAndExpression ( OrOr logicalAndExpression )* + ; + +logicalAndExpression + : inclusiveOrExpression ( AndAnd inclusiveOrExpression )* + ; + +inclusiveOrExpression + : exclusiveOrExpression ( Or exclusiveOrExpression )* + ; + +exclusiveOrExpression + : andExpression ( Caret andExpression )* + ; + +andExpression + : equalityExpression ( And equalityExpression )* + ; + +equalityExpression + : relationalExpression ( ( Equal | NotEqual ) relationalExpression )* + ; + +relationalExpression + : shiftExpression ( ( Less | Greater | LessEqual | GreaterEqual ) shiftExpression )* + ; + +shiftExpression + : additiveExpression ( ( LeftShift | RightShift ) additiveExpression )* + ; + +additiveExpression + : multiplicativeExpression ( ( Plus | Minus ) multiplicativeExpression )* + ; + +multiplicativeExpression + : castExpression ( ( Star | Div | Mod ) castExpression )* + ; + +castExpression + : LeftParen typeName RightParen castExpression + | unaryExpression + ; + +unaryExpression + : postfixExpression + | PlusPlus unaryExpression + | MinusMinus unaryExpression + | unaryOperator castExpression + | Sizeof ( unaryExpression | LeftParen typeName RightParen ) + | Alignof LeftParen typeName RightParen + | BuiltinVaArg LeftParen unaryExpression Comma typeName RightParen + | BuiltinOffsetof LeftParen typeName Comma unaryExpression RightParen + ; + +unaryOperator + : And | Star | Plus | Minus | Tilde | Not + ; + +// --------------------------------------------------------------------------- +// Postfix expressions — KEY RULE for detecting function calls +// --------------------------------------------------------------------------- + +postfixExpression + : primaryExpression + | postfixExpression LeftBracket expression RightBracket // array index + | postfixExpression LeftParen argumentExpressionList? RightParen // FUNCTION CALL ← detect this + | postfixExpression ( Dot | Arrow ) Identifier // member access + | postfixExpression ( PlusPlus | MinusMinus ) + | LeftParen typeName RightParen LeftBrace initializerList Comma? RightBrace + ; + +argumentExpressionList + : assignmentExpression ( Comma assignmentExpression )* + ; + +primaryExpression + : Identifier + | Constant + | StringLiteral+ + | LeftParen expression RightParen + | genericSelection + ; + +Constant + : IntegerConstant + | FloatingConstant + | CharacterConstant + ; + +genericSelection + : Generic LeftParen assignmentExpression Comma genericAssocList RightParen + ; + +genericAssocList + : genericAssociation ( Comma genericAssociation )* + ; + +genericAssociation + : ( typeName | Default ) Colon assignmentExpression + ; + +constantExpression + : conditionalExpression + ; From 11d43d8e5c7c020cb9c3793fdc91570f203ba843 Mon Sep 17 00:00:00 2001 From: Divyateja Indrakanti Date: Wed, 6 May 2026 17:36:01 +0530 Subject: [PATCH 06/17] Create CppCheck.java Signed-off-by: Divyateja Indrakanti --- .../com/ibm/engine/language/cpp/CppCheck.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 engine/src/main/java/com/ibm/engine/language/cpp/CppCheck.java diff --git a/engine/src/main/java/com/ibm/engine/language/cpp/CppCheck.java b/engine/src/main/java/com/ibm/engine/language/cpp/CppCheck.java new file mode 100644 index 00000000..23ab965e --- /dev/null +++ b/engine/src/main/java/com/ibm/engine/language/cpp/CppCheck.java @@ -0,0 +1,36 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2025 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.cpp; + +/** + * Marker interface for C/C++ detection rules. + * + *

This interface fills the {@code R} (Rule) generic type parameter used throughout the engine. + * It is the C/C++ equivalent of {@code JavaCheck} in the Java language support and {@code + * CSharpCheck} in the C# language support. + * + *

All C/C++ detection rule classes should implement this interface so that the engine's generic + * machinery can operate in a type-safe way across language modules. + */ +public interface CppCheck { + // Marker interface — no methods required. + // The engine uses this as a type bound: IDetectionRule, + // ILanguageSupport, etc. +} From b9be3a6501d42ae167f1595ac91f7a1a59b742cc Mon Sep 17 00:00:00 2001 From: Divyateja Indrakanti Date: Wed, 6 May 2026 17:36:46 +0530 Subject: [PATCH 07/17] Create CppSymbol.java Signed-off-by: Divyateja Indrakanti --- .../ibm/engine/language/cpp/CppSymbol.java | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 engine/src/main/java/com/ibm/engine/language/cpp/CppSymbol.java diff --git a/engine/src/main/java/com/ibm/engine/language/cpp/CppSymbol.java b/engine/src/main/java/com/ibm/engine/language/cpp/CppSymbol.java new file mode 100644 index 00000000..e06489eb --- /dev/null +++ b/engine/src/main/java/com/ibm/engine/language/cpp/CppSymbol.java @@ -0,0 +1,109 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2025 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.cpp; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Represents a symbol (variable, function, or type name) in C/C++ source code. + * + *

This class fills the {@code S} (Symbol) generic type parameter used throughout the engine. It + * is the C/C++ equivalent of {@code Symbol} in the Java language support and {@code CSharpSymbol} + * in the C# language support. + * + *

In C/C++, because we use ANTLR for parsing (not a full Sonar language analyzer), we do not + * have a rich semantic model with full type resolution. This class therefore provides a lightweight + * symbol representation based on the identifier name and its resolved string value (if any). + * + *

Known limitation: unlike Java's {@code Symbol}, this class does not support cross-method + * variable tracking. Symbol resolution is limited to single-function scope, consistent with the + * current C# implementation approach. + */ +public final class CppSymbol { + + /** The name of the identifier as it appears in source code (e.g., {@code "ctx"}, {@code "key"}). */ + @Nonnull private final String name; + + /** + * The resolved string value of this symbol, if it can be statically determined. For example, + * if the code contains {@code const char *algo = "AES-256-CBC"}, then {@code resolvedValue} + * would be {@code "AES-256-CBC"}. {@code null} if the value cannot be statically resolved. + */ + @Nullable private final String resolvedValue; + + /** + * Creates a new {@code CppSymbol} with a name and no resolved value. + * + * @param name the identifier name as it appears in source code + */ + public CppSymbol(@Nonnull String name) { + this.name = name; + this.resolvedValue = null; + } + + /** + * Creates a new {@code CppSymbol} with both a name and a statically resolved value. + * + * @param name the identifier name as it appears in source code + * @param resolvedValue the statically resolved string value, or {@code null} if not resolvable + */ + public CppSymbol(@Nonnull String name, @Nullable String resolvedValue) { + this.name = name; + this.resolvedValue = resolvedValue; + } + + /** + * Returns the identifier name as it appears in source code. + * + * @return the symbol name, never {@code null} + */ + @Nonnull + public String name() { + return name; + } + + /** + * Returns the statically resolved string value of this symbol, if available. + * + * @return the resolved value, or {@code null} if it cannot be statically determined + */ + @Nullable + public String resolvedValue() { + return resolvedValue; + } + + /** + * Returns {@code true} if this symbol has a statically resolved value. + * + * @return {@code true} if {@link #resolvedValue()} is non-null + */ + public boolean isResolved() { + return resolvedValue != null; + } + + @Override + public String toString() { + if (resolvedValue != null) { + return "CppSymbol{name='" + name + "', resolvedValue='" + resolvedValue + "'}"; + } + return "CppSymbol{name='" + name + "'}"; + } +} From 5231abd5a19dea0e7fae147a197af09407028add Mon Sep 17 00:00:00 2001 From: Divyateja Indrakanti Date: Wed, 6 May 2026 17:54:32 +0530 Subject: [PATCH 08/17] Create CppTree.java Signed-off-by: Divyateja Indrakanti --- .../ibm/engine/language/cpp/tree/CppTree.java | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 engine/src/main/java/com/ibm/engine/language/cpp/tree/CppTree.java diff --git a/engine/src/main/java/com/ibm/engine/language/cpp/tree/CppTree.java b/engine/src/main/java/com/ibm/engine/language/cpp/tree/CppTree.java new file mode 100644 index 00000000..5e3d2c6b --- /dev/null +++ b/engine/src/main/java/com/ibm/engine/language/cpp/tree/CppTree.java @@ -0,0 +1,99 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2025 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.cpp.tree; + +/** + * Base interface for all C/C++ abstract syntax tree (AST) nodes used by the engine. + * + *

This interface fills the {@code T} (Tree) generic type parameter used throughout the engine. + * It is the C/C++ equivalent of {@code Tree} in the Java language support (from the Sonar Java + * plugin API) and {@code CSharpTree} in the C# language support. + * + *

Because we use ANTLR to parse C/C++ source files directly (rather than a Sonar language + * plugin), we build our own lightweight tree node hierarchy rather than relying on a pre-existing + * AST API. Each concrete implementation of this interface represents a specific syntactic construct + * in C/C++ source code. + * + *

The concrete tree node types are: + * + *

    + *
  • {@link CppLiteralTree} — string literals and numeric constants + *
  • {@link CppIdentifierTree} — variable and function names + *
  • {@link CppMethodInvocationTree} — function call expressions + *
  • {@link CppMemberAccessTree} — {@code ->} and {@code .} member access + *
  • {@link CppBlockTree} — compound statement / code block + *
+ * + *

The {@link Kind} enum allows the engine and detection rules to distinguish between node types + * without needing to use {@code instanceof} checks everywhere. + */ +public interface CppTree { + + /** + * Enumerates the kinds of C/C++ tree nodes recognised by the engine. + * + *

This mirrors the role of the {@code Tree.Kind} enum in the Sonar Java plugin API. + */ + enum Kind { + /** A string literal ({@code "AES-256-CBC"}) or numeric constant ({@code 256}). */ + LITERAL, + + /** A bare identifier — a variable name or function name used as an expression. */ + IDENTIFIER, + + /** + * A function call expression, e.g. {@code EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), + * NULL, key, iv)}. + */ + METHOD_INVOCATION, + + /** + * A member access expression using {@code ->} or {@code .}, e.g. {@code ctx->cipher} or + * {@code params.key_len}. + */ + MEMBER_ACCESS, + + /** A compound statement enclosed in braces, i.e. a {@code { }} block. */ + BLOCK, + } + + /** + * Returns the kind of this tree node. + * + * @return the {@link Kind} of this node, never {@code null} + */ + Kind kind(); + + /** + * Returns the 1-based line number in the source file where this node begins. + * + *

Used by the engine to record the detection location (file + line) in the CBOM output. + * + * @return line number ≥ 1 + */ + int line(); + + /** + * Returns the 0-based character offset within its line where this node begins. + * + * @return column offset ≥ 0 + */ + int column(); +} From 3c3735015ebeb63240ee89197495ef62fa5e2f89 Mon Sep 17 00:00:00 2001 From: Divyateja Indrakanti Date: Thu, 7 May 2026 11:12:41 +0530 Subject: [PATCH 09/17] Create Cppliteraltree.java Signed-off-by: Divyateja Indrakanti --- .../language/cpp/tree/Cppliteraltree.java | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 engine/src/main/java/com/ibm/engine/language/cpp/tree/Cppliteraltree.java diff --git a/engine/src/main/java/com/ibm/engine/language/cpp/tree/Cppliteraltree.java b/engine/src/main/java/com/ibm/engine/language/cpp/tree/Cppliteraltree.java new file mode 100644 index 00000000..78a3b4ee --- /dev/null +++ b/engine/src/main/java/com/ibm/engine/language/cpp/tree/Cppliteraltree.java @@ -0,0 +1,130 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2025 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.cpp.tree; + +import javax.annotation.Nonnull; + +/** + * A C/C++ tree node representing a literal value — either a string literal or a numeric constant. + * + *

Examples of what this node captures: + * + *

    + *
  • String literal used as an algorithm name: {@code "AES-256-CBC"}, {@code "SHA256"} + *
  • Integer constant used as a key size: {@code 256}, {@code 2048}, {@code 128} + *
  • Hex constant: {@code 0x100} + *
+ * + *

This is one of the most important node types for crypto detection, because OpenSSL functions + * frequently receive algorithm identifiers as string literals or key sizes as integer constants. + * + *

Example C code producing this node: + * + *

{@code
+ * EVP_DigestInit_ex(ctx, EVP_get_digestbyname("SHA256"), NULL);
+ * RSA_generate_key_ex(rsa, 2048, e, NULL);
+ * }
+ */ +public final class CppLiteralTree implements CppTree { + + /** The raw text of the literal exactly as it appears in source, e.g. {@code "AES-256-CBC"} or {@code 256}. */ + @Nonnull private final String value; + + /** Whether this literal is a string (quoted) rather than a numeric constant. */ + private final boolean isString; + + private final int line; + private final int column; + + /** + * Creates a new {@code CppLiteralTree}. + * + * @param value the raw literal text as it appears in source code (including quotes for strings) + * @param isString {@code true} if this is a string literal, {@code false} for numeric + * @param line 1-based line number in the source file + * @param column 0-based column offset within the line + */ + public CppLiteralTree(@Nonnull String value, boolean isString, int line, int column) { + this.value = value; + this.isString = isString; + this.line = line; + this.column = column; + } + + /** + * Returns the raw text of the literal as it appears in source code. + * + *

For string literals this includes the surrounding quotes, e.g. {@code "AES-256-CBC"}. + * Call {@link #unquotedValue()} to get the content without quotes. + * + * @return the raw literal text, never {@code null} + */ + @Nonnull + public String value() { + return value; + } + + /** + * Returns the string content without surrounding double-quotes. + * + *

For example, if the source code contained {@code "AES-256-CBC"}, this method returns + * {@code AES-256-CBC}. + * + *

For numeric literals, this is the same as {@link #value()}. + * + * @return the unquoted value + */ + @Nonnull + public String unquotedValue() { + if (isString && value.length() >= 2 && value.startsWith("\"") && value.endsWith("\"")) { + return value.substring(1, value.length() - 1); + } + return value; + } + + /** + * Returns {@code true} if this literal is a string (enclosed in double quotes). + * + * @return {@code true} for string literals, {@code false} for numeric constants + */ + public boolean isString() { + return isString; + } + + @Override + public Kind kind() { + return Kind.LITERAL; + } + + @Override + public int line() { + return line; + } + + @Override + public int column() { + return column; + } + + @Override + public String toString() { + return "CppLiteralTree{value=" + value + ", line=" + line + "}"; + } +} From 0801c31780406e5271c8461aaef50acf49462989 Mon Sep 17 00:00:00 2001 From: Divyateja Indrakanti Date: Thu, 7 May 2026 11:14:20 +0530 Subject: [PATCH 10/17] Create CppIdentifierTree.java Signed-off-by: Divyateja Indrakanti --- .../language/cpp/tree/CppIdentifierTree.java | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 engine/src/main/java/com/ibm/engine/language/cpp/tree/CppIdentifierTree.java diff --git a/engine/src/main/java/com/ibm/engine/language/cpp/tree/CppIdentifierTree.java b/engine/src/main/java/com/ibm/engine/language/cpp/tree/CppIdentifierTree.java new file mode 100644 index 00000000..a994a330 --- /dev/null +++ b/engine/src/main/java/com/ibm/engine/language/cpp/tree/CppIdentifierTree.java @@ -0,0 +1,136 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2025 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.cpp.tree; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * A C/C++ tree node representing an identifier — a variable name, function name, or type name as + * it appears in an expression. + * + *

Examples of identifiers this node captures: + * + *

    + *
  • A function name used in a call: {@code EVP_aes_256_cbc} in {@code EVP_aes_256_cbc()} + *
  • A variable passed as an argument: {@code ctx}, {@code key}, {@code iv} + *
  • A macro constant: {@code EVP_MAX_KEY_LENGTH}, {@code RSA_PKCS1_PADDING} + *
+ * + *

Example C code producing this node: + * + *

{@code
+ * EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+ * //               ^^^                  ^^^
+ * //        identifier (variable)   identifier (function)
+ * }
+ * + *

The engine uses this node to match function names against detection rules. When the detection + * engine sees a {@link CppMethodInvocationTree}, it checks the callee identifier name against the + * patterns defined in each {@code IDetectionRule}. + */ +public final class CppIdentifierTree implements CppTree { + + /** The identifier name exactly as it appears in source code. */ + @Nonnull private final String name; + + /** + * The optional type string of this identifier, derived from context (e.g. {@code + * "EVP_CIPHER_CTX *"} for a pointer variable). This is used by the engine's {@code + * forObjectTypes} matching. May be {@code null} when the type cannot be inferred from the + * current single-method scope. + */ + @Nullable private final String type; + + private final int line; + private final int column; + + /** + * Creates a new {@code CppIdentifierTree} without type information. + * + * @param name the identifier name as it appears in source code + * @param line 1-based line number in the source file + * @param column 0-based column offset within the line + */ + public CppIdentifierTree(@Nonnull String name, int line, int column) { + this.name = name; + this.type = null; + this.line = line; + this.column = column; + } + + /** + * Creates a new {@code CppIdentifierTree} with optional type information. + * + * @param name the identifier name as it appears in source code + * @param type the inferred type string, or {@code null} if not known + * @param line 1-based line number in the source file + * @param column 0-based column offset within the line + */ + public CppIdentifierTree(@Nonnull String name, @Nullable String type, int line, int column) { + this.name = name; + this.type = type; + this.line = line; + this.column = column; + } + + /** + * Returns the identifier name exactly as it appears in source code. + * + * @return identifier name, never {@code null} + */ + @Nonnull + public String name() { + return name; + } + + /** + * Returns the inferred type of this identifier, if available. + * + *

For OpenSSL code, this would be something like {@code "EVP_CIPHER_CTX"} or {@code "RSA"}. + * This is used by the engine when matching {@code forObjectTypes(...)} in detection rules. + * + * @return the type string, or {@code null} if not determinable from single-method scope + */ + @Nullable + public String type() { + return type; + } + + @Override + public Kind kind() { + return Kind.IDENTIFIER; + } + + @Override + public int line() { + return line; + } + + @Override + public int column() { + return column; + } + + @Override + public String toString() { + return "CppIdentifierTree{name='" + name + "', type='" + type + "', line=" + line + "}"; + } +} From 54da66be4c08d277011042fcae7cb37c24371cb8 Mon Sep 17 00:00:00 2001 From: Divyateja Indrakanti Date: Thu, 7 May 2026 11:15:40 +0530 Subject: [PATCH 11/17] Create Cppmemberaccesstree.java Signed-off-by: Divyateja Indrakanti --- .../cpp/tree/Cppmemberaccesstree.java | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 engine/src/main/java/com/ibm/engine/language/cpp/tree/Cppmemberaccesstree.java diff --git a/engine/src/main/java/com/ibm/engine/language/cpp/tree/Cppmemberaccesstree.java b/engine/src/main/java/com/ibm/engine/language/cpp/tree/Cppmemberaccesstree.java new file mode 100644 index 00000000..f48be0dc --- /dev/null +++ b/engine/src/main/java/com/ibm/engine/language/cpp/tree/Cppmemberaccesstree.java @@ -0,0 +1,164 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2025 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.cpp.tree; + +import javax.annotation.Nonnull; + +/** + * A C/C++ tree node representing a member access expression using {@code ->} or {@code .}. + * + *

In C, struct fields and pointer-to-struct fields are frequently used when working with OpenSSL + * types. This node captures expressions like: + * + *

    + *
  • Pointer member access: {@code ctx->cipher}, {@code rsa->n}, {@code params->key_len} + *
  • Direct member access: {@code suite.algorithm}, {@code config.padding} + *
+ * + *

Example C code producing this node: + * + *

{@code
+ * // Arrow operator (pointer to struct)
+ * EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+ * ctx->encrypt = 1;
+ *
+ * // Dot operator (direct struct access)
+ * RSA_METHOD method;
+ * method.rsa_sign = my_sign_function;
+ * }
+ * + *

The engine uses this node primarily to resolve the object type when matching {@code + * forObjectTypes(...)} in detection rules. By tracking what type {@code ctx} is, the engine can + * confirm that {@code EVP_EncryptInit_ex(ctx, ...)} is indeed operating on an {@code + * EVP_CIPHER_CTX}. + */ +public final class CppMemberAccessTree implements CppTree { + + /** Enum distinguishing between {@code ->} (arrow) and {@code .} (dot) access. */ + public enum AccessType { + /** Pointer member access via {@code ->}. */ + ARROW, + /** Direct struct member access via {@code .}. */ + DOT + } + + /** The expression on the left side of the operator, e.g. {@code ctx} in {@code ctx->cipher}. */ + @Nonnull private final CppTree object; + + /** The member name on the right side of the operator, e.g. {@code "cipher"} in {@code ctx->cipher}. */ + @Nonnull private final String memberName; + + /** Whether this is an arrow ({@code ->}) or dot ({@code .}) access. */ + @Nonnull private final AccessType accessType; + + private final int line; + private final int column; + + /** + * Creates a new {@code CppMemberAccessTree}. + * + * @param object the tree node representing the left-hand side expression + * @param memberName the name of the struct member being accessed + * @param accessType whether this is {@code ->} or {@code .} access + * @param line 1-based line number in the source file + * @param column 0-based column offset within the line + */ + public CppMemberAccessTree( + @Nonnull CppTree object, + @Nonnull String memberName, + @Nonnull AccessType accessType, + int line, + int column) { + this.object = object; + this.memberName = memberName; + this.accessType = accessType; + this.line = line; + this.column = column; + } + + /** + * Returns the tree node representing the object (left-hand side of the access operator). + * + * @return the object expression tree, never {@code null} + */ + @Nonnull + public CppTree object() { + return object; + } + + /** + * Returns the name of the struct member being accessed. + * + * @return member name, never {@code null} + */ + @Nonnull + public String memberName() { + return memberName; + } + + /** + * Returns whether this is a {@code ->} (arrow) or {@code .} (dot) member access. + * + * @return the access type, never {@code null} + */ + @Nonnull + public AccessType accessType() { + return accessType; + } + + /** Returns {@code true} if this is a {@code ->} (pointer) member access. */ + public boolean isArrow() { + return accessType == AccessType.ARROW; + } + + /** Returns {@code true} if this is a {@code .} (direct) member access. */ + public boolean isDot() { + return accessType == AccessType.DOT; + } + + @Override + public Kind kind() { + return Kind.MEMBER_ACCESS; + } + + @Override + public int line() { + return line; + } + + @Override + public int column() { + return column; + } + + @Override + public String toString() { + String op = accessType == AccessType.ARROW ? "->" : "."; + return "CppMemberAccessTree{object=" + + object + + ", op='" + + op + + "', member='" + + memberName + + "', line=" + + line + + "}"; + } +} From 67a550600a1fb280b4e6c242604038566b3287d0 Mon Sep 17 00:00:00 2001 From: Divyateja Indrakanti Date: Sun, 10 May 2026 08:44:47 +0530 Subject: [PATCH 12/17] Create Cppscancontext.java Signed-off-by: Divyateja Indrakanti --- .../engine/language/cpp/Cppscancontext.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 engine/src/main/java/com/ibm/engine/language/cpp/Cppscancontext.java diff --git a/engine/src/main/java/com/ibm/engine/language/cpp/Cppscancontext.java b/engine/src/main/java/com/ibm/engine/language/cpp/Cppscancontext.java new file mode 100644 index 00000000..c3018e7d --- /dev/null +++ b/engine/src/main/java/com/ibm/engine/language/cpp/Cppscancontext.java @@ -0,0 +1,85 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2025 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.cpp; + +import com.ibm.engine.language.IScanContext; +import com.ibm.engine.language.cpp.tree.CppTree; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +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/C++ scan context wrapping the SonarQube {@link SensorContext}. + * + *

Mirrors {@link com.ibm.engine.language.csharp.CSharpScanContext}: since there is no + * sonar-cxx framework integration, we hold the raw {@link SensorContext} and {@link InputFile} + * and report issues directly via the SonarQube sensor API. + * + *

This class fills the {@code P} (Publisher) generic type parameter used throughout the engine. + * + * @param sensorContext the SonarQube sensor context for the current analysis run + * @param inputFile the C/C++ source file currently being analysed + * @param repositoryKey the rule repository key (e.g. {@code "sonar-cpp-crypto"}) + */ +public record CppScanContext( + @Nonnull SensorContext sensorContext, + @Nonnull InputFile inputFile, + @Nonnull String repositoryKey) + implements IScanContext { + + @Override + public void reportIssue( + @Nonnull CppCheck currentRule, + @Nonnull CppTree tree, + @Nonnull String message) { + String ruleKey = getRuleKey(currentRule); + if (ruleKey == null) { + return; + } + int line = Math.max(1, tree.line()); + 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 CppCheck 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(); + } +} From bd46f657062ffe4843abc3d46b8e14df2699fc31 Mon Sep 17 00:00:00 2001 From: Divyateja Indrakanti Date: Sun, 10 May 2026 08:45:21 +0530 Subject: [PATCH 13/17] Create Cpplanguagetranslation.java Signed-off-by: Divyateja Indrakanti --- .../language/cpp/Cpplanguagetranslation.java | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 engine/src/main/java/com/ibm/engine/language/cpp/Cpplanguagetranslation.java diff --git a/engine/src/main/java/com/ibm/engine/language/cpp/Cpplanguagetranslation.java b/engine/src/main/java/com/ibm/engine/language/cpp/Cpplanguagetranslation.java new file mode 100644 index 00000000..0685640d --- /dev/null +++ b/engine/src/main/java/com/ibm/engine/language/cpp/Cpplanguagetranslation.java @@ -0,0 +1,149 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2025 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.cpp; + +import com.ibm.engine.detection.IType; +import com.ibm.engine.detection.MatchContext; +import com.ibm.engine.language.ILanguageTranslation; +import com.ibm.engine.language.cpp.tree.CppIdentifierTree; +import com.ibm.engine.language.cpp.tree.CppLiteralTree; +import com.ibm.engine.language.cpp.tree.CppMemberAccessTree; +import com.ibm.engine.language.cpp.tree.CppMethodInvocationTree; +import com.ibm.engine.language.cpp.tree.CppTree; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import javax.annotation.Nonnull; + +/** + * Language translation implementation for C/C++. + * + *

Mirrors {@link com.ibm.engine.language.csharp.CSharpLanguageTranslation}: extracts method + * names, object type strings, and parameter information from the {@link CppTree} hierarchy produced + * by the ANTLR4-based tree converter. + * + *

Since ANTLR4 provides only syntactic information (no full C/C++ type inference), all parameter + * types are treated as matching any expected type. Object type matching is string-based: the engine + * matches the inferred type name (e.g. {@code "EVP_CIPHER_CTX"}) against the type strings declared + * in detection rules. + * + *

For C/C++ there are no constructors in the Java/C# sense — all initialization goes through + * function calls like {@code EVP_CIPHER_CTX_new()} — so {@code forConstructor()} is not used. + * The {@code ""} sentinel is therefore never returned here. + */ +public final class CppLanguageTranslation implements ILanguageTranslation { + + @Nonnull + @Override + public Optional getMethodName( + @Nonnull MatchContext matchContext, @Nonnull CppTree methodInvocation) { + if (methodInvocation instanceof CppMethodInvocationTree invocation) { + return Optional.of(invocation.methodName()); + } + return Optional.empty(); + } + + @Nonnull + @Override + public Optional getInvokedObjectTypeString( + @Nonnull MatchContext matchContext, @Nonnull CppTree methodInvocation) { + if (methodInvocation instanceof CppMethodInvocationTree invocation) { + String typeName = invocation.objectType(); + if (typeName == null) { + // No inferred object type — match any type to avoid blocking detection + return Optional.of(expectedType -> true); + } + return Optional.of(expectedType -> expectedType.equals(typeName)); + } + return Optional.empty(); + } + + @Nonnull + @Override + public Optional getMethodReturnTypeString( + @Nonnull MatchContext matchContext, @Nonnull CppTree methodInvocation) { + // ANTLR4 provides no type inference; return type unavailable + return Optional.empty(); + } + + @Nonnull + @Override + public List getMethodParameterTypes( + @Nonnull MatchContext matchContext, @Nonnull CppTree methodInvocation) { + if (!(methodInvocation instanceof CppMethodInvocationTree invocation)) { + return Collections.emptyList(); + } + List args = invocation.arguments(); + if (args.isEmpty()) { + return Collections.emptyList(); + } + // No semantic type info available from ANTLR4 — every argument matches any expected type + List types = new ArrayList<>(args.size()); + for (int i = 0; i < args.size(); i++) { + types.add(expectedType -> true); + } + return types; + } + + @Nonnull + @Override + public Optional resolveIdentifierAsString( + @Nonnull MatchContext matchContext, @Nonnull CppTree identifierTree) { + if (identifierTree instanceof CppLiteralTree literal) { + // Return the unquoted value so string literals like "AES-256-CBC" + // are resolved as AES-256-CBC (without surrounding quotes) + return Optional.of(literal.unquotedValue()); + } else if (identifierTree instanceof CppIdentifierTree identifier) { + return Optional.of(identifier.name()); + } + return Optional.empty(); + } + + @Nonnull + @Override + public Optional getEnumIdentifierName( + @Nonnull MatchContext matchContext, @Nonnull CppTree enumIdentifier) { + // C has no enum member-access syntax like C# (CipherMode.CBC). + // Enum constants appear as plain identifiers (EVP_CIPH_CBC_MODE) + // or as member accesses on a struct (params->mode). + if (enumIdentifier instanceof CppMemberAccessTree memberAccess) { + return Optional.of(memberAccess.memberName()); + } else if (enumIdentifier instanceof CppIdentifierTree identifier) { + return Optional.of(identifier.name()); + } + return Optional.empty(); + } + + @Nonnull + @Override + public Optional getEnumClassName( + @Nonnull MatchContext matchContext, @Nonnull CppTree enumClass) { + // C does not have qualified enum syntax like C#. + // Member access object side is returned as a best-effort "class" name. + if (enumClass instanceof CppMemberAccessTree memberAccess) { + CppTree object = memberAccess.object(); + if (object instanceof CppIdentifierTree identifier) { + return Optional.of(identifier.name()); + } + } + return Optional.empty(); + } +} From 92b759aa7f7938cd63280094ea7ab0d35bff63e9 Mon Sep 17 00:00:00 2001 From: Divyateja Indrakanti Date: Sun, 10 May 2026 08:46:04 +0530 Subject: [PATCH 14/17] Create Cppbasemethodvisitor.java Signed-off-by: Divyateja Indrakanti --- .../language/cpp/Cppbasemethodvisitor.java | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 engine/src/main/java/com/ibm/engine/language/cpp/Cppbasemethodvisitor.java diff --git a/engine/src/main/java/com/ibm/engine/language/cpp/Cppbasemethodvisitor.java b/engine/src/main/java/com/ibm/engine/language/cpp/Cppbasemethodvisitor.java new file mode 100644 index 00000000..c12c8b41 --- /dev/null +++ b/engine/src/main/java/com/ibm/engine/language/cpp/Cppbasemethodvisitor.java @@ -0,0 +1,67 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2025 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.cpp; + +import com.ibm.engine.detection.IBaseMethodVisitor; +import com.ibm.engine.detection.IDetectionEngine; +import com.ibm.engine.detection.TraceSymbol; +import com.ibm.engine.language.cpp.tree.CppBlockTree; +import com.ibm.engine.language.cpp.tree.CppTree; +import javax.annotation.Nonnull; + +/** + * Base method visitor for C/C++ that invokes the detection engine on each function body. + * + *

Mirrors {@link com.ibm.engine.language.csharp.CSharpBaseMethodVisitor}: when the sensor + * dispatches a function body (a {@link CppBlockTree}) via {@link #visitMethodDefinition}, the + * detection engine is run on it. + * + *

In C/C++ every top-level function body is a {@link CppBlockTree}. The ANTLR tree converter + * ({@code CppTreeConverter}) extracts one {@link CppBlockTree} per function body, and the sensor + * calls {@link #visitMethodDefinition} once per block. The detection engine then walks the block's + * statement list looking for function calls that match registered detection rules. + */ +public final class CppBaseMethodVisitor implements IBaseMethodVisitor { + + @Nonnull private final TraceSymbol traceSymbol; + @Nonnull private final IDetectionEngine detectionEngine; + + public CppBaseMethodVisitor( + @Nonnull TraceSymbol traceSymbol, + @Nonnull IDetectionEngine detectionEngine) { + this.traceSymbol = traceSymbol; + this.detectionEngine = detectionEngine; + } + + /** + * Visits a C/C++ function body and runs the detection engine on it. + * + *

Only {@link CppBlockTree} nodes are processed — any other tree type is ignored because + * C/C++ detection rules operate at the block (function body) level. + * + * @param method a tree node representing a function body, expected to be a {@link CppBlockTree} + */ + @Override + public void visitMethodDefinition(@Nonnull CppTree method) { + if (method instanceof CppBlockTree blockTree) { + detectionEngine.run(traceSymbol, blockTree); + } + } +} From 46516e5e97d776e7f2eaea391902f20c5d7cde40 Mon Sep 17 00:00:00 2001 From: Divyateja Indrakanti Date: Sun, 10 May 2026 08:50:03 +0530 Subject: [PATCH 15/17] Update LanguageSupporter.java Signed-off-by: Divyateja Indrakanti --- .../com/ibm/engine/language/LanguageSupporter.java | 11 +++++++++++ 1 file changed, 11 insertions(+) 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 c5a82a55..cfa3685c 100644 --- a/engine/src/main/java/com/ibm/engine/language/LanguageSupporter.java +++ b/engine/src/main/java/com/ibm/engine/language/LanguageSupporter.java @@ -24,6 +24,11 @@ import com.ibm.engine.language.csharp.CSharpScanContext; import com.ibm.engine.language.csharp.CSharpSymbol; import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.language.cpp.CppCheck; +import com.ibm.engine.language.cpp.CppLanguageSupport; +import com.ibm.engine.language.cpp.CppScanContext; +import com.ibm.engine.language.cpp.CppSymbol; +import com.ibm.engine.language.cpp.tree.CppTree; import com.ibm.engine.language.go.GoLanguageSupport; import com.ibm.engine.language.go.GoScanContext; import com.ibm.engine.language.java.JavaLanguageSupport; @@ -73,4 +78,10 @@ public static ILanguageSupport goLanguageS csharpLanguageSupporter() { return new CSharpLanguageSupport(); } + + @Nonnull + public static ILanguageSupport + cppLanguageSupporter() { + return new CppLanguageSupport(); + } } From 9b942bac15241cff967da2be040fac4422cb19a3 Mon Sep 17 00:00:00 2001 From: Divyateja Indrakanti Date: Sun, 10 May 2026 08:57:49 +0530 Subject: [PATCH 16/17] Create Cpplanguagesupport.java Signed-off-by: Divyateja Indrakanti --- .../language/cpp/Cpplanguagesupport.java | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 engine/src/main/java/com/ibm/engine/language/cpp/Cpplanguagesupport.java diff --git a/engine/src/main/java/com/ibm/engine/language/cpp/Cpplanguagesupport.java b/engine/src/main/java/com/ibm/engine/language/cpp/Cpplanguagesupport.java new file mode 100644 index 00000000..339b5321 --- /dev/null +++ b/engine/src/main/java/com/ibm/engine/language/cpp/Cpplanguagesupport.java @@ -0,0 +1,118 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2025 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.cpp; + +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.language.cpp.tree.CppMethodInvocationTree; +import com.ibm.engine.language.cpp.tree.CppTree; +import com.ibm.engine.rule.IDetectionRule; +import java.util.Optional; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Language support implementation for C/C++. + * + *

Mirrors {@link com.ibm.engine.language.csharp.CSharpLanguageSupport}: wires together the + * {@link CppLanguageTranslation}, {@link CppDetectionEngine}, and {@link CppBaseMethodVisitor} + * using the shared {@link Handler} and {@link DetectionExecutive} infrastructure. + * + *

This is the top-level factory that the rest of the plugin uses for C/C++ scanning. It is + * registered in {@link com.ibm.engine.language.LanguageSupporter#cppLanguageSupporter()}. + */ +public final class CppLanguageSupport + implements ILanguageSupport { + + @Nonnull + private final Handler handler; + + @Nonnull + private final CppLanguageTranslation translation; + + public CppLanguageSupport() { + this.handler = new Handler<>(this); + this.translation = new CppLanguageTranslation(); + } + + @Nonnull + @Override + public ILanguageTranslation translation() { + return translation; + } + + @Nonnull + @Override + public DetectionExecutive + createDetectionExecutive( + @Nonnull CppTree 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 CppDetectionEngine(detectionStore, this.handler); + } + + @Nonnull + @Override + public IBaseMethodVisitorFactory getBaseMethodVisitorFactory() { + return CppBaseMethodVisitor::new; + } + + @Nonnull + @Override + public Optional getEnclosingMethod(@Nonnull CppTree expression) { + if (expression instanceof CppMethodInvocationTree invocation + && invocation.enclosingBlock() != null) { + return Optional.of(invocation.enclosingBlock()); + } + return Optional.empty(); + } + + @Nullable + @Override + public MethodMatcher createMethodMatcherBasedOn( + @Nonnull CppTree methodDefinition) { + // Inter-procedural method matching not supported without semantic analysis + return null; + } + + @Nullable + @Override + public EnumMatcher createSimpleEnumMatcherFor( + @Nonnull CppTree enumIdentifier, @Nonnull MatchContext matchContext) { + Optional name = translation().getEnumIdentifierName(matchContext, enumIdentifier); + return name.>map(EnumMatcher::new).orElse(null); + } +} From 3c68d58e8a9680f4b13b8774015fed31f53ef3ac Mon Sep 17 00:00:00 2001 From: Divyateja Indrakanti Date: Sun, 10 May 2026 08:58:47 +0530 Subject: [PATCH 17/17] Create Cppdetectionengine.java Signed-off-by: Divyateja Indrakanti --- .../language/cpp/Cppdetectionengine.java | 389 ++++++++++++++++++ 1 file changed, 389 insertions(+) create mode 100644 engine/src/main/java/com/ibm/engine/language/cpp/Cppdetectionengine.java diff --git a/engine/src/main/java/com/ibm/engine/language/cpp/Cppdetectionengine.java b/engine/src/main/java/com/ibm/engine/language/cpp/Cppdetectionengine.java new file mode 100644 index 00000000..6f3ca39d --- /dev/null +++ b/engine/src/main/java/com/ibm/engine/language/cpp/Cppdetectionengine.java @@ -0,0 +1,389 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2025 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.cpp; + +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.detection.ValueDetection; +import com.ibm.engine.language.cpp.tree.CppBlockTree; +import com.ibm.engine.language.cpp.tree.CppIdentifierTree; +import com.ibm.engine.language.cpp.tree.CppLiteralTree; +import com.ibm.engine.language.cpp.tree.CppMemberAccessTree; +import com.ibm.engine.language.cpp.tree.CppMethodInvocationTree; +import com.ibm.engine.language.cpp.tree.CppTree; +import com.ibm.engine.model.factory.IValueFactory; +import com.ibm.engine.rule.DetectableParameter; +import com.ibm.engine.rule.DetectionRule; +import com.ibm.engine.rule.MethodDetectionRule; +import com.ibm.engine.rule.Parameter; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Detection engine implementation for C/C++. + * + *

Mirrors {@link com.ibm.engine.language.csharp.CSharpDetectionEngine}: walks a {@link + * CppBlockTree} looking for {@link CppMethodInvocationTree} nodes that match the active detection + * rule, then emits detections and resolves argument values. + * + *

Symbol resolution is intentionally minimal — ANTLR4 provides no semantic type inference. + * Only literal values ({@link CppLiteralTree}), member access expressions ({@link + * CppMemberAccessTree}), and direct identifiers ({@link CppIdentifierTree}) are resolved. + * + *

Limitations (same as C# implementation): + *

    + *
  • No cross-method variable tracking — only single-function scope. + *
  • No inter-procedural argument mapping. + *
  • No return value resolution without type inference. + *
+ */ +@SuppressWarnings("java:S3776") +public final class CppDetectionEngine implements IDetectionEngine { + + @Nonnull + private final DetectionStore detectionStore; + + @Nonnull + private final Handler handler; + + public CppDetectionEngine( + @Nonnull DetectionStore detectionStore, + @Nonnull Handler handler) { + this.detectionStore = detectionStore; + this.handler = handler; + } + + @Override + public void run(@Nonnull CppTree tree) { + run(TraceSymbol.createStart(), tree); + } + + @Override + public void run(@Nonnull TraceSymbol traceSymbol, @Nonnull CppTree tree) { + if (tree instanceof CppBlockTree blockTree) { + for (CppTree statement : blockTree.statements()) { + processStatement(traceSymbol, statement); + } + } else if (tree instanceof CppMethodInvocationTree invocation) { + if (traceSymbol.is(TraceSymbol.State.SYMBOL) + && !isInvocationOnVariable(invocation, traceSymbol)) { + return; + } + handler.addCallToCallStack(invocation, detectionStore.getScanContext()); + if (detectionStore + .getDetectionRule() + .match(invocation, handler.getLanguageSupport().translation())) { + analyseMethodInvocation(invocation); + } + } + } + + private void processStatement( + @Nonnull TraceSymbol traceSymbol, @Nonnull CppTree statement) { + if (statement instanceof CppMethodInvocationTree invocation) { + if (traceSymbol.is(TraceSymbol.State.SYMBOL) + && !isInvocationOnVariable(invocation, traceSymbol)) { + return; + } + handler.addCallToCallStack(invocation, detectionStore.getScanContext()); + if (detectionStore + .getDetectionRule() + .match(invocation, handler.getLanguageSupport().translation())) { + analyseMethodInvocation(invocation); + } + } + } + + // ------------------------------------------------------------------------- + // Invocation analysis + // ------------------------------------------------------------------------- + + private void analyseMethodInvocation(@Nonnull CppMethodInvocationTree invocation) { + DetectionRule rule = emitDetectionAndGetRule(invocation); + if (rule == null) { + return; + } + processParameters(rule.parameters(), invocation.arguments(), invocation); + } + + @SuppressWarnings("unchecked") + @Nullable + private DetectionRule emitDetectionAndGetRule(@Nonnull CppTree tree) { + if (detectionStore.getDetectionRule().is(MethodDetectionRule.class)) { + detectionStore.onReceivingNewDetection(new MethodDetection<>(tree, null)); + return null; + } + DetectionRule detectionRule = + (DetectionRule) detectionStore.getDetectionRule(); + if (detectionRule.actionFactory() != null) { + detectionStore.onReceivingNewDetection(new MethodDetection<>(tree, null)); + } + return detectionRule; + } + + private void processParameters( + @Nonnull List> parameters, + @Nonnull List arguments, + @Nonnull CppTree parentTree) { + int index = 0; + for (Parameter parameter : parameters) { + if (index >= arguments.size()) { + break; + } + processParameter(parameter, arguments.get(index), parentTree); + index++; + } + } + + @SuppressWarnings("unchecked") + private void processParameter( + @Nonnull Parameter parameter, + @Nonnull CppTree expression, + @Nonnull CppTree parentTree) { + if (parameter.is(DetectableParameter.class)) { + DetectableParameter detectable = + (DetectableParameter) parameter; + List> resolved = + resolveValuesInInnerScope( + Object.class, expression, detectable.getiValueFactory()); + if (resolved.isEmpty()) { + resolveValuesInOuterScope(expression, detectable); + } else { + resolved.stream() + .map(rv -> new ValueDetection<>(rv, detectable, parentTree, parentTree)) + .forEach(detectionStore::onReceivingNewDetection); + } + } else if (!parameter.getDetectionRules().isEmpty()) { + dispatchDependingParameter(parameter, expression); + } + } + + private void dispatchDependingParameter( + @Nonnull Parameter parameter, @Nonnull CppTree expression) { + if (expression instanceof CppMethodInvocationTree invocation) { + detectionStore.onDetectedDependingParameter( + parameter, invocation, DetectionStore.Scope.EXPRESSION); + } else { + detectionStore.onDetectedDependingParameter( + parameter, expression, DetectionStore.Scope.EXPRESSION); + } + } + + // ------------------------------------------------------------------------- + // Value resolution + // ------------------------------------------------------------------------- + + @Nonnull + @Override + public List> resolveValuesInInnerScope( + @Nonnull Class clazz, + @Nonnull CppTree expression, + @Nullable IValueFactory valueFactory) { + return resolveValues(clazz, expression, new LinkedList<>()); + } + + @Nonnull + @SuppressWarnings("unchecked") + private List> resolveValues( + @Nonnull Class clazz, + @Nonnull CppTree tree, + @Nonnull LinkedList selections) { + if (selections.size() > 15) { + return Collections.emptyList(); + } + + // String or numeric literal + if (tree instanceof CppLiteralTree literal) { + String value = literal.unquotedValue(); + Optional resolved = resolveConstant(clazz, value); + return resolved + .map(v -> List.of(new ResolvedValue<>(v, tree))) + .orElse(Collections.emptyList()); + } + + // Member access: e.g. EVP_CIPHER_CTX->type → "type" + if (tree instanceof CppMemberAccessTree memberAccess) { + selections.addFirst(memberAccess); + String memberName = memberAccess.memberName(); + Optional resolved = resolveConstant(clazz, memberName); + if (resolved.isPresent()) { + return List.of(new ResolvedValue<>(resolved.get(), tree)); + } + return Collections.emptyList(); + } + + // Bare identifier — resolve its name as a string + if (tree instanceof CppIdentifierTree identifier) { + Optional resolved = resolveConstant(clazz, identifier.name()); + return resolved + .map(v -> List.of(new ResolvedValue<>(v, tree))) + .orElse(Collections.emptyList()); + } + + return Collections.emptyList(); + } + + @Nonnull + @SuppressWarnings("unchecked") + private Optional resolveConstant(@Nonnull Class clazz, @Nullable String value) { + if (value == null) { + return Optional.empty(); + } + try { + if (clazz == String.class) { + return Optional.of(clazz.cast(value)); + } + if (clazz == Integer.class || clazz == Object.class) { + try { + Integer intValue = Integer.parseInt(value); + if (clazz == Integer.class) { + return Optional.of(clazz.cast(intValue)); + } + return Optional.of((O) intValue); + } catch (NumberFormatException e) { + // not a number — fall through + } + } + if (clazz == Object.class) { + return Optional.of((O) value); + } + return Optional.empty(); + } catch (ClassCastException e) { + return Optional.empty(); + } + } + + @Override + public void resolveValuesInOuterScope( + @Nonnull CppTree expression, @Nonnull Parameter parameter) { + // Cross-scope resolution not supported without semantic analysis + } + + @Override + public void resolveMethodReturnValues( + @Nonnull Class clazz, + @Nonnull CppTree methodDefinition, + @Nonnull Parameter parameter) { + // Return value resolution not supported without type inference + } + + @Nullable + @Override + public ResolvedValue resolveEnumValue( + @Nonnull Class clazz, + @Nonnull CppTree enumClassDefinition, + @Nonnull LinkedList selections) { + // Enum class definition lookup not supported + return null; + } + + // ------------------------------------------------------------------------- + // Symbol tracking + // ------------------------------------------------------------------------- + + @Nonnull + @Override + public Optional> getAssignedSymbol(@Nonnull CppTree expression) { + if (expression instanceof CppMethodInvocationTree invocation) { + // For C: EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + // The assigned variable name is tracked via CppMethodInvocationTree.assignedVariable() + String assigned = invocation.assignedVariable(); + if (assigned != null) { + return Optional.of(TraceSymbol.createFrom(new CppSymbol(assigned))); + } + } + return Optional.empty(); + } + + @Nonnull + @Override + public Optional> getMethodInvocationParameterSymbol( + @Nonnull CppTree methodInvocation, @Nonnull Parameter parameter) { + if (methodInvocation instanceof CppMethodInvocationTree invocation) { + List args = invocation.arguments(); + int idx = parameter.getIndex(); + if (idx >= 0 && idx < args.size()) { + return Optional.of(TraceSymbol.createWithStateNoSymbol()); + } + return Optional.of(TraceSymbol.createWithStateDifferent()); + } + return Optional.empty(); + } + + @Nonnull + @Override + public Optional> getNewClassParameterSymbol( + @Nonnull CppTree newClass, @Nonnull Parameter parameter) { + // C/C++ has no 'new' keyword equivalent — constructor-like calls are + // standard functions (e.g. EVP_CIPHER_CTX_new()) handled as method invocations + return Optional.of(TraceSymbol.createWithStateDifferent()); + } + + @Override + public boolean isInvocationOnVariable( + CppTree methodInvocation, @Nonnull TraceSymbol variableSymbol) { + if (!(methodInvocation instanceof CppMethodInvocationTree invocation)) { + return false; + } + CppSymbol sym = variableSymbol.getSymbol(); + if (sym == null) { + return false; + } + // The objectType holds the inferred receiver — matches when it equals the variable name + // e.g. "ctx" in EVP_EncryptInit_ex(ctx, ...) matching TraceSymbol("ctx") + String objectType = invocation.objectType(); + return objectType != null && objectType.equals(sym.name()); + } + + @Override + public boolean isInitForVariable( + CppTree newClass, @Nonnull TraceSymbol variableSymbol) { + if (!(newClass instanceof CppMethodInvocationTree invocation)) { + return false; + } + String assignedId = invocation.assignedVariable(); + if (assignedId == null) { + return false; + } + CppSymbol sym = variableSymbol.getSymbol(); + if (sym == null) { + return false; + } + return assignedId.equals(sym.name()); + } + + @Nullable + @Override + public CppTree extractArgumentFromMethodCaller( + @Nonnull CppTree methodDefinition, + @Nonnull CppTree methodInvocation, + @Nonnull CppTree methodParameterIdentifier) { + // Inter-procedural argument mapping not supported + return null; + } +}