Skip to content

Comments

Enhance secure password analyzer using semantic API#1427

Open
sdmdg wants to merge 10 commits intoballerina-platform:masterfrom
sdmdg:feature/fix-secure-password-analyzer
Open

Enhance secure password analyzer using semantic API#1427
sdmdg wants to merge 10 commits intoballerina-platform:masterfrom
sdmdg:feature/fix-secure-password-analyzer

Conversation

@sdmdg
Copy link

@sdmdg sdmdg commented Jan 23, 2026

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:

  • Implemented Semantic API for precise type-checking of MySQL client objects to prevent false positives.
  • Added support for positional arguments (identifying password at the 3rd index) and named arguments.
  • Introduced password strength validation (flags empty, short, or simple passwords).

Fixes: N/A

Examples

import ballerinax/mysql as db;

// Alias & Keyword Resilience
// Flagged: Positional detection (index 2) with alias and 'check' keyword
db:Client c1 = check new ("localhost", "root", "", "database");
// Flagged: Explicit new expression with 'checkpanic'
var c2 = checkpanic new db:Client(host = "localhost", password = "admin");


// Argument Handling (Named vs Positional)
// Flagged: Named argument correctly identified even when out of order
db:Client|error c3 = new (password = "12345", user = "root", host = "localhost");
// Flagged: Standard positional argument at 3rd index
db:Client|error c4 = new ("localhost", "root", "password");


// Security Logic & False Positive Prevention
// NOT Flagged: Strong password satisfies security requirements
db:Client c5 = check new ("localhost", "user", "Safe#Pass@2026!");
// NOT Flagged: Correctly ignored via Semantic API (Type is MockClient, not mysql:Client)
MockClient mock = new ("localhost", "user", ""); 
// Helper for False Positive Test
class MockClient {
    public function init(string h, string u, string p) {}
}

Checklist

  • Linked to an issue
  • Updated the specification
  • Added tests
  • Updated the changelog
  • Checked native-image compatibility

nureka-rodrigo and others added 9 commits January 23, 2026 09:25
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.
@CLAassistant
Copy link

CLAassistant commented Jan 23, 2026

CLA assistant check
All committers have signed the CLA.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

argIndex++;
}
} catch (RuntimeException e) {
// Prevent crashing the Scanner
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
// 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);

Copilot uses AI. Check for mistakes.
Comment on lines 107 to 140
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;
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documented. I've added a comment to the logic explaining that we currently only support static string literals.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants