Enhance secure password analyzer using semantic API#1427
Enhance secure password analyzer using semantic API#1427sdmdg wants to merge 10 commits intoballerina-platform:masterfrom
Conversation
…aticCodeAnalyzerTest
Updated the file paths in rule<x>.json to use relative paths instead of absolute paths. This change improves portability and ensures that the rules can be used in different environments without modification.
Introduces a new Ballerina file 'custom_prefix.bal' under rule1 test resources and updates the expected output to include a vulnerability finding for insecure database password usage. This enhances test coverage for custom import prefixes in the static code analyzer.
There was a problem hiding this comment.
Pull request overview
This PR enhances the SecurePasswordAnalyzer in the MySQL compiler plugin to use the Ballerina Semantic API instead of syntax-based checks. This enables accurate identification of mysql:Client objects even when module aliases (e.g., import ballerinax/mysql as m) or keywords like check/checkpanic are used. The analyzer now validates password strength by checking for empty, short (< 8 chars), or simple passwords (missing uppercase, lowercase, digits, or special characters).
Changes:
- Migrated SecurePasswordAnalyzer from syntax-based pattern matching to Semantic API for precise type checking
- Added support for both positional arguments (password at index 2) and named arguments (password parameter)
- Implemented password strength validation with specific criteria
- Updated test infrastructure to use scan-command TestRunner API instead of process-based testing
- Added comprehensive test cases covering named arguments, positional arguments, and custom module prefixes
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| gradle.properties | Added Jackson Databind dependency and upgraded balScan version to 0.10.0 |
| SecurePasswordAnalyzer.java | Completely rewritten to use Semantic API for type checking, added argument handling for both named and positional parameters, implemented password strength validation logic |
| RuleFactory.java | Made class final to prevent extension (utility class best practice) |
| MySQLStaticCodeAnalyzer.java | Updated to register analyzer for IMPLICIT_NEW_EXPRESSION and EXPLICIT_NEW_EXPRESSION in addition to FUNCTION_CALL |
| MySQLRule.java | Changed getRule() method to getDescription() for better clarity |
| StaticCodeAnalyzerTest.java | Refactored from process-based testing to API-based testing using TestRunner, but contains incorrect test validation |
| ProcessOutputGobbler.java | Removed as it's no longer needed with API-based testing approach |
| rule1.json | Updated expected output to include 11 comprehensive test scenarios across 3 test files |
| pos_arg.bal | New test file with 4 test cases for positional argument scenarios |
| named_arg.bal | New test file with 4 test cases for named argument scenarios |
| custom_prefix.bal | New test file with 4 test cases testing module alias support |
| main.bal | Removed old single test case file |
Comments suppressed due to low confidence (1)
compiler-plugin/src/main/java/io/ballerina/stdlib/mysql/compiler/staticcodeanalyzer/RuleFactory.java:31
- The RuleFactory class is now marked as final, which is good for a utility class. However, it's missing a private constructor to prevent instantiation, which is inconsistent with other utility classes in the codebase such as Constants (line 30-31 in Constants.java). Consider adding a private constructor to RuleFactory to match this convention and fully prevent instantiation.
public final class RuleFactory {
public static Rule createRule(int id, String description, RuleKind kind) {
return new RuleImpl(id, description, kind);
}
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
.../test/java/io/ballerina/stdlib/mysql/compiler/staticcodeanalyzer/StaticCodeAnalyzerTest.java
Outdated
Show resolved
Hide resolved
| argIndex++; | ||
| } | ||
| } catch (RuntimeException e) { | ||
| // Prevent crashing the Scanner |
There was a problem hiding this comment.
The catch block silently swallows all RuntimeExceptions without logging or reporting them. While the comment states this is to "Prevent crashing the Scanner," this makes debugging extremely difficult when issues occur. At minimum, the exception should be logged (e.g., using a logger or stderr) to help diagnose problems during development and production. Consider logging the exception with relevant context about which node was being analyzed.
| // Prevent crashing the Scanner | |
| // Prevent crashing the Scanner, but log the exception for debugging | |
| System.err.println("SecurePasswordAnalyzer: RuntimeException while analyzing node: " | |
| + context.node()); | |
| e.printStackTrace(System.err); |
| private boolean isWeakPassword(ExpressionNode expression) { | ||
| if (expression instanceof BasicLiteralNode basicLiteralNode) { | ||
| String text = basicLiteralNode.literalToken().text(); | ||
|
|
||
| if (text.length() >= 2 && text.startsWith("\"") && text.endsWith("\"")) { | ||
| text = text.substring(1, text.length() - 1); | ||
| } | ||
|
|
||
| if (text.isEmpty()) { | ||
| return true; | ||
| } | ||
| if (text.length() < 8) { | ||
| return true; | ||
| } | ||
|
|
||
| boolean hasUpper = false; | ||
| boolean hasLower = false; | ||
| boolean hasDigit = false; | ||
| boolean hasSpecial = false; | ||
|
|
||
| for (char c : text.toCharArray()) { | ||
| if (Character.isUpperCase(c)) { | ||
| hasUpper = true; | ||
| } else if (Character.isLowerCase(c)) { | ||
| hasLower = true; | ||
| } else if (Character.isDigit(c)) { | ||
| hasDigit = true; | ||
| } else { | ||
| hasSpecial = true; | ||
| } | ||
| } | ||
| return !(hasUpper && hasLower && hasDigit && hasSpecial); | ||
| } | ||
| return false; |
There was a problem hiding this comment.
The password validation only checks BasicLiteralNode (string literals) and returns false for all other expression types (line 139-140). This means that passwords passed as variables, function calls, or other expression types are automatically considered "strong" and never flagged, even if they might be weak at runtime. While it's challenging to validate dynamic values at compile-time, the current behavior silently ignores these cases. Consider documenting this limitation or adding a comment explaining why non-literal passwords are not validated.
There was a problem hiding this comment.
Documented. I've added a comment to the logic explaining that we currently only support static string literals.
Purpose
This PR enhances the SecurePasswordAnalyzer within the MySQL compiler plugin to provide security validation for client initializations. By transitioning from a syntax-based check to the Ballerina Semantic API, the analyzer now accurately identifies mysql:Client objects even when module aliases or check/checkpanic keywords are used.
Key Changes:
Fixes: N/A
Examples
Checklist