CoolFace
Apppublic

Deeps-2005/java-ssl-scanner

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
AutoPatcher.java608 linesDownload Raw Back to java_analyzer
1import com.github.javaparser.StaticJavaParser;
2import com.github.javaparser.ast.CompilationUnit;
3import com.github.javaparser.ast.body.*;
4import com.github.javaparser.ast.expr.*;
5import com.github.javaparser.ast.stmt.*;
6import com.github.javaparser.ast.visitor.ModifierVisitor;
7import com.github.javaparser.ast.NodeList;
8import com.github.javaparser.ast.type.ClassOrInterfaceType;
9import com.github.javaparser.ast.type.Type;
10import com.github.javaparser.ast.comments.LineComment;
11import com.github.javaparser.ast.comments.BlockComment;
12import com.github.javaparser.ast.Node;
13
14import java.io.File;
15import java.io.FileInputStream;
16import java.io.IOException;
17import java.util.Arrays;
18import java.util.List;
19import java.util.Set;
20import java.util.HashSet;
21import java.util.ArrayList;
22import java.util.Comparator;
23import java.util.Map;
24import java.util.LinkedHashMap;
25
26public class AutoPatcher {
27
28    // Lists for quick lookup, shared across methods to reduce object creation
29    private static final List<String> INSECURE_PROTOCOLS = Arrays.asList("sslv2", "sslv3", "tlsv1", "tlsv1.0", "tlsv1.1");
30    private static final List<String> WEAK_CIPHER_KEYWORDS = Arrays.asList("null", "anon", "export", "rc4", "des", "md5");
31    private static final List<String> RECOMMENDED_PROTOCOLS = Arrays.asList("TLSv1.2", "TLSv1.3");
32    private static final List<String> RECOMMENDED_CIPHER_SUITES = Arrays.asList(
33        "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
34        "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
35        "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
36        "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
37    );
38
39    // NEW: Cryptography and XML related constants
40    private static final List<String> WEAK_HASHING_ALGORITHMS = Arrays.asList("md5", "sha-1");
41    private static final Set<String> XML_FACTORIES = new HashSet<>(Arrays.asList(
42        "DocumentBuilderFactory", "SAXParserFactory", "XMLInputFactory"
43    ));
44    // Pattern for hardcoded sensitive strings (password, key, secret, salt, token, auth)
45    private static final String SENSITIVE_STRING_PATTERN = ".*(password|pass|secret|key|pwd|salt|token|auth).*";
46
47
48    // Using LinkedHashMap to maintain insertion order for more readable logs
49    private static Map<Integer, String> patchLogs = new LinkedHashMap<>(); // Store logs here
50
51    // Consolidated strong cipher/protocol string literals for parsing
52    private static final String STRONG_CIPHERS_ARRAY_EXPR =
53        "new String[]{\"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\", " +
54        "\"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\"}";
55
56    private static final String STRONG_PROTOCOLS_ARRAY_EXPR =
57        "new String[]{\"TLSv1.2\", \"TLSv1.3\"}";
58
59
60    public static void main(String[] args) throws Exception {
61        if (args.length == 0) {
62            System.err.println("Usage: java AutoPatcher <JavaFile>");
63            System.err.println("This tool attempts to automatically patch known SSL/JSSE vulnerabilities.");
64            System.err.println("The patched code will be printed to standard output.");
65            return;
66        }
67
68        String filePath = args[0];
69        CompilationUnit cu;
70        try (FileInputStream in = new FileInputStream(filePath)) {
71            cu = StaticJavaParser.parse(in);
72        } catch (IOException e) {
73            System.err.println("Error reading file: " + filePath + " - " + e.getMessage());
74            return;
75        }
76
77        System.err.println("Attempting to patch: " + filePath);
78
79        // Apply patches using the visitor
80        cu.accept(new SecurityPatchVisitor(), null);
81
82        // Print ONLY the patched code to standard output (stdout)
83        System.out.println(cu.toString());
84
85        // Print patch logs to standard error (stderr), wrapped in markers
86        System.err.println("--- PATCH LOG START ---");
87        // Sort logs by line number before printing for consistent output
88        List<Map.Entry<Integer, String>> sortedLogs = new ArrayList<>(patchLogs.entrySet());
89        sortedLogs.sort(Comparator.comparingInt(Map.Entry::getKey));
90
91        for (Map.Entry<Integer, String> entry : sortedLogs) {
92            System.err.println("Line " + entry.getKey() + ": " + entry.getValue());
93        }
94        System.err.println("--- PATCH LOG END ---");
95    }
96
97    private static class SecurityPatchVisitor extends ModifierVisitor<Void> {
98
99        /**
100         * Helper method to record patch logs.
101         * @param line The line number where the patch was applied.
102         * @param message A description of the patch.
103         */
104        private void logPatch(int line, String message) {
105            patchLogs.put(line, message);
106        }
107
108        /**
109         * Visits MethodCallExpr nodes to apply patches related to method calls.
110         * This includes system property settings, protocol enabling, hostname verification,
111         * keystore password loading, weak hashing algorithms, and XML factory instantiation.
112         *
113         * @param mce The MethodCallExpr node being visited.
114         * @param arg A generic argument (not used here).
115         * @return The modified MethodCallExpr (or null if the node is removed).
116         */
117        @Override
118        public MethodCallExpr visit(MethodCallExpr mce, Void arg) {
119            super.visit(mce, arg); // Call super to ensure full traversal and allow nested modifications
120
121            int line = mce.getBegin().map(p -> p.line).orElse(-1);
122
123            // 1. Patch: Remove debug logging and TLS renegotiation system properties
124            if (mce.getNameAsString().equals("setProperty") &&
125                mce.getArguments().size() == 2) {
126                Expression arg0 = mce.getArgument(0);
127                if (arg0.isStringLiteralExpr()) {
128                    String key = arg0.asStringLiteralExpr().getValue();
129                    if (key.equals("javax.net.debug") || key.equals("com.ibm.jsse2.renegotiate") || key.equals("jdk.tls.rejectClientInitiatedRenegotiation")) {
130                        logPatch(line, "Removed System.setProperty(\"" + key + "\", ...) for security.");
131                        return null; // Removing the method call expression
132                    }
133                }
134            }
135
136            // 2. Patch: Insecure HostnameVerifier (lambda or anonymous class always returns true)
137            if (mce.getNameAsString().equals("setDefaultHostnameVerifier") &&
138                mce.getArguments().size() == 1) {
139                Expression argExpr = mce.getArgument(0);
140                boolean patched = false;
141
142                if (argExpr.isLambdaExpr()) {
143                    LambdaExpr lambda = argExpr.asLambdaExpr();
144                    if ((lambda.getBody().isExpressionStmt() && lambda.getBody().asExpressionStmt().getExpression().isBooleanLiteralExpr() &&
145                         lambda.getBody().asExpressionStmt().getExpression().asBooleanLiteralExpr().getValue()) ||
146                        (lambda.getBody().isBlockStmt() && lambda.getBody().asBlockStmt().getStatements().stream()
147                            .filter(stmt -> stmt instanceof ReturnStmt)
148                            .map(stmt -> (ReturnStmt) stmt)
149                            .anyMatch(returnStmt -> returnStmt.getExpression().isPresent() && returnStmt.getExpression().get().isBooleanLiteralExpr() &&
150                                                     returnStmt.getExpression().get().asBooleanLiteralExpr().getValue()))) {
151                        // Replace with a safer placeholder that requires manual config
152                        mce.setArgument(0, StaticJavaParser.parseExpression("new javax.net.ssl.HostnameVerifier() {\n" +
153                            "    @Override\n" +
154                            "    public boolean verify(String hostname, javax.net.ssl.SSLSession session) {\n" +
155                            "        // AUTO-PATCH: Manual review required. Implement strict hostname verification here.\n" +
156                            "        // Example: return hostname.equals(\"your.secure.domain.com\");\n" +
157                            "        return false; // Default to false for security until reviewed\n" +
158                            "    }\n" +
159                            "}"));
160                        patched = true;
161                    }
162                } else if (argExpr.isObjectCreationExpr()) {
163                    ObjectCreationExpr oce = argExpr.asObjectCreationExpr();
164                    if (oce.getType().getNameAsString().equals("HostnameVerifier") && oce.getAnonymousClassBody().isPresent()) {
165                        if (oce.getAnonymousClassBody().get().stream()
166                            .filter(bodyDecl -> bodyDecl instanceof MethodDeclaration)
167                            .map(bodyDecl -> (MethodDeclaration) bodyDecl)
168                            .filter(md -> md.getNameAsString().equals("verify") && md.getBody().isPresent())
169                            .anyMatch(md -> md.getBody().get().getStatements().isEmpty() ||
170                                            md.getBody().get().getStatements().stream()
171                                                .filter(stmt -> stmt instanceof ReturnStmt)
172                                                .map(stmt -> (ReturnStmt) stmt)
173                                                .anyMatch(returnStmt -> returnStmt.getExpression().isPresent() && returnStmt.getExpression().get().isBooleanLiteralExpr() &&
174                                                                         returnStmt.getExpression().get().asBooleanLiteralExpr().getValue()))) {
175                            // Replace with a safer placeholder that requires manual config
176                            mce.setArgument(0, StaticJavaParser.parseExpression("new javax.net.ssl.HostnameVerifier() {\n" +
177                                "    @Override\n" +
178                                "    public boolean verify(String hostname, javax.net.ssl.SSLSession session) {\n" +
179                                "        // AUTO-PATCH: Manual review required. Implement strict hostname verification here.\n" +
180                                "        // Example: return hostname.equals(\"your.secure.domain.com\");\n" +
181                                "        return false; // Default to false for security until reviewed\n" +
182                                "    }\n" +
183                                "}"));
184                            patched = true;
185                        }
186                    }
187                }
188                if (patched) {
189                    logPatch(line, "Insecure HostnameVerifier replaced with secure placeholder requiring manual review.");
190                }
191            }
192
193            // 3. Patch: Hardcoded password passed to keystore load()
194            if (mce.getNameAsString().equals("load") &&
195                mce.getScope().isPresent() &&
196                mce.getScope().get().toString().contains("KeyStore") &&
197                mce.getArguments().size() == 2 &&
198                (mce.getArgument(1).isCharLiteralExpr() || mce.getArgument(1).isStringLiteralExpr())) {
199                logPatch(line, "Hardcoded password in KeyStore.load() replaced with environment variable lookup.");
200                mce.setArgument(1, StaticJavaParser.parseExpression("System.getenv(\"KEYSTORE_PASSWORD\").toCharArray()"));
201            }
202
203            // 4. Patch: Use of outdated/weak SSL/TLS protocols in SSLContext.getInstance()
204            if (mce.getNameAsString().equals("getInstance") &&
205                mce.getScope().isPresent() &&
206                mce.getScope().get().toString().contains("SSLContext") &&
207                mce.getArguments().size() >= 1 &&
208                mce.getArgument(0).isStringLiteralExpr()) {
209                String protocol = mce.getArgument(0).asStringLiteralExpr().getValue().toLowerCase();
210                if (INSECURE_PROTOCOLS.contains(protocol)) {
211                    logPatch(line, "Insecure SSLContext protocol '" + protocol.toUpperCase() + "' changed to 'TLSv1.2'.");
212                    mce.setArgument(0, StaticJavaParser.parseExpression("\"TLSv1.2\""));
213                }
214            }
215
216            // 5. Patch: Enabling weak/outdated protocols via setEnabledProtocols()
217            if (mce.getNameAsString().equals("setEnabledProtocols") &&
218                mce.getScope().isPresent() &&
219                (mce.getScope().get().toString().contains("SSLSocket") || mce.getScope().get().toString().contains("SSLEngine"))) {
220                
221                Expression protocolsArg = mce.getArgument(0);
222                boolean shouldPatch = false;
223
224                if (protocolsArg.isArrayInitializerExpr()) {
225                    ArrayInitializerExpr init = protocolsArg.asArrayInitializerExpr();
226                    for (Expression expr : init.getValues()) {
227                        if (expr.isStringLiteralExpr()) {
228                            String protocol = expr.asStringLiteralExpr().getValue().toLowerCase();
229                            if (INSECURE_PROTOCOLS.contains(protocol)) {
230                                shouldPatch = true;
231                                break;
232                            }
233                        }
234                    }
235                } else if (protocolsArg.isStringLiteralExpr()) {
236                    String protocol = protocolsArg.asStringLiteralExpr().getValue().toLowerCase();
237                    if (INSECURE_PROTOCOLS.contains(protocol)) {
238                        shouldPatch = true;
239                    }
240                }
241                
242                if (shouldPatch) {
243                    logPatch(line, "setEnabledProtocols() to use only 'TLSv1.2' and 'TLSv1.3'.");
244                    mce.setArgument(0, StaticJavaParser.parseExpression(STRONG_PROTOCOLS_ARRAY_EXPR));
245                }
246            }
247
248            // 6. Patch: Enabling weak CIPHER SUITES via setEnabledCipherSuites()
249            if (mce.getNameAsString().equals("setEnabledCipherSuites") &&
250                mce.getScope().isPresent() &&
251                (mce.getScope().get().toString().contains("SSLSocket") || mce.getScope().get().toString().contains("SSLEngine"))) {
252                
253                Expression ciphersArg = mce.getArgument(0);
254                boolean shouldPatch = false;
255
256                if (ciphersArg.isArrayInitializerExpr()) {
257                    ArrayInitializerExpr init = ciphersArg.asArrayInitializerExpr();
258                    for (Expression expr : init.getValues()) {
259                        if (expr.isStringLiteralExpr()) {
260                            String cipherSuite = expr.asStringLiteralExpr().getValue().toLowerCase();
261                            if (WEAK_CIPHER_KEYWORDS.stream().anyMatch(cipherSuite::contains)) {
262                                shouldPatch = true;
263                                break;
264                            }
265                        }
266                    }
267                } else if (ciphersArg.isStringLiteralExpr()) {
268                    String cipherSuite = ciphersArg.asStringLiteralExpr().getValue().toLowerCase();
269                    if (WEAK_CIPHER_KEYWORDS.stream().anyMatch(cipherSuite::contains)) {
270                        shouldPatch = true;
271                    }
272                }
273                
274                if (shouldPatch) {
275                    logPatch(line, "setEnabledCipherSuites() to use strong default cipher suites.");
276                    mce.setArgument(0, StaticJavaParser.parseExpression(STRONG_CIPHERS_ARRAY_EXPR));
277                }
278            }
279
280            // NEW PATCH: Weak Hashing Algorithms (MD5, SHA-1)
281            if (mce.getNameAsString().equals("getInstance") &&
282                mce.getScope().isPresent() &&
283                mce.getScope().get().toString().contains("MessageDigest") &&
284                mce.getArguments().size() >= 1) {
285                Expression arg0 = mce.getArgument(0);
286                if (arg0.isStringLiteralExpr()) {
287                    String algorithm = arg0.asStringLiteralExpr().getValue().toLowerCase();
288                    if (WEAK_HASHING_ALGORITHMS.contains(algorithm)) {
289                        logPatch(line, "Weak hashing algorithm '" + algorithm.toUpperCase() + "' updated to 'SHA-256'.");
290                        mce.setArgument(0, StaticJavaParser.parseExpression("\"SHA-256\""));
291                    }
292                }
293            }
294
295            // NEW PATCH: XML External Entity (XXE) Vulnerabilities - Add hardening features
296            if (mce.getNameAsString().equals("newInstance") &&
297                mce.getScope().isPresent() &&
298                XML_FACTORIES.contains(mce.getScope().get().toString())) {
299                
300                // Find the variable declaration for this factory
301                mce.findAncestor(VariableDeclarator.class).ifPresent(vd -> {
302                    String factoryName = vd.getNameAsString();
303                    // Append hardening calls after the factory creation
304                    BlockStmt parentBlock = mce.findAncestor(BlockStmt.class).orElse(null);
305                    if (parentBlock != null) {
306                        int insertIndex = parentBlock.getStatements().indexOf(mce.findAncestor(ExpressionStmt.class).orElse(null));
307                        if (insertIndex != -1) {
308                            NodeList<Statement> newStatements = new NodeList<>();
309                            newStatements.add(StaticJavaParser.parseStatement(factoryName + ".setFeature(\"http://apache.org/xml/features/disallow-doctype-decl\", true);"));
310                            newStatements.add(StaticJavaParser.parseStatement(factoryName + ".setFeature(\"http://xml.org/sax/features/external-general-entities\", false);"));
311                            newStatements.add(StaticJavaParser.parseStatement(factoryName + ".setFeature(\"http://xml.org/sax/features/external-parameter-entities\", false);"));
312                            newStatements.add(StaticJavaParser.parseStatement(factoryName + ".setFeature(\"http://apache.org/xml/features/nonvalidating/load-external-dtd\", false);"));
313                            newStatements.add(StaticJavaParser.parseStatement(factoryName + ".setXIncludeAware(false);"));
314                            newStatements.add(StaticJavaParser.parseStatement(factoryName + ".setExpandEntityReferences(false);"));
315                            
316                            for (int i = 0; i < newStatements.size(); i++) {
317                                parentBlock.addStatement(insertIndex + 1 + i, newStatements.get(i));
318                            }
319                            logPatch(line, "Added XXE hardening features for XML factory: " + factoryName);
320                        }
321                    }
322                });
323            }
324
325            // NEW PATCH: Insecure HTTP URL Usage - Change to HTTPS
326            if (mce.getNameAsString().equals("URL") && mce.getArguments().size() == 1) {
327                Expression arg0 = mce.getArgument(0);
328                if (arg0.isStringLiteralExpr()) {
329                    String urlString = arg0.asStringLiteralExpr().getValue();
330                    if (urlString.startsWith("http://") && !urlString.contains("localhost") && !urlString.contains("127.0.0.1")) {
331                        String httpsUrlString = urlString.replaceFirst("http://", "https://");
332                        mce.setArgument(0, StaticJavaParser.parseExpression("\"" + httpsUrlString + "\""));
333                        logPatch(line, "Changed insecure 'http://' URL to 'https://': " + httpsUrlString);
334                    }
335                }
336            }
337
338
339            return mce;
340        }
341
342        /**
343         * Visits ObjectCreationExpr nodes to apply patches during object instantiation.
344         * This includes insecure TrustManager implementations, unseeded SecureRandom,
345         * hardcoded cryptographic keys, and deserialization of untrusted data.
346         *
347         * @param oce The ObjectCreationExpr node being visited.
348         * @param arg A generic argument (not used here).
349         * @return The modified ObjectCreationExpr.
350         */
351        @Override
352        public ObjectCreationExpr visit(ObjectCreationExpr oce, Void arg) {
353            super.visit(oce, arg); // Call super to ensure full traversal
354
355            int line = oce.getBegin().map(p -> p.line).orElse(-1);
356
357            // Patch: Insecure TrustManager (anonymous class with empty methods or return true)
358            if (oce.getAnonymousClassBody().isPresent() &&
359                (oce.getType().getNameAsString().equals("X509TrustManager") ||
360                 oce.getType().getNameAsString().equals("TrustManager"))) {
361
362                boolean foundInsecurePattern = false;
363                for (BodyDeclaration bodyDecl : oce.getAnonymousClassBody().get()) {
364                    if (bodyDecl instanceof MethodDeclaration) {
365                        MethodDeclaration md = (MethodDeclaration) bodyDecl;
366                        String methodName = md.getNameAsString();
367
368                        if (("checkClientTrusted".equals(methodName) || "checkServerTrusted".equals(methodName)) && md.getBody().isPresent()) {
369                            BlockStmt methodBody = md.getBody().get();
370                            
371                            // Check for empty body or body with 'return true'
372                            if (methodBody.getStatements().isEmpty() ||
373                                methodBody.getStatements().stream()
374                                    .filter(stmt -> stmt instanceof ReturnStmt)
375                                    .map(stmt -> (ReturnStmt) stmt)
376                                    .anyMatch(returnStmt -> returnStmt.getExpression().isPresent() && returnStmt.getExpression().get().isBooleanLiteralExpr() &&
377                                                             returnStmt.getExpression().get().asBooleanLiteralExpr().getValue())) {
378                                foundInsecurePattern = true;
379                            }
380                            // Check for swallowing exceptions
381                            else if (methodBody.getStatements().stream()
382                                .filter(stmt -> stmt instanceof TryStmt)
383                                .map(stmt -> (TryStmt) stmt)
384                                .anyMatch(ts -> ts.getCatchClauses().stream()
385                                    .anyMatch(catchClause -> {
386                                        Type caughtType = catchClause.getParameter().getType();
387                                        if (caughtType instanceof ClassOrInterfaceType) {
388                                            String typeName = ((ClassOrInterfaceType) caughtType).getNameAsString();
389                                            if (typeName.equals("Exception") || typeName.equals("Throwable") ||
390                                                typeName.equals("CertificateException") || typeName.equals("NoSuchAlgorithmException")) {
391                                                return catchClause.getBody().getStatements().isEmpty() ||
392                                                       (catchClause.getBody().getStatements().size() == 1 &&
393                                                        catchClause.getBody().getStatement(0).isExpressionStmt() &&
394                                                        catchClause.getBody().getStatement(0).asExpressionStmt().getExpression().isMethodCallExpr() &&
395                                                        catchClause.getBody().getStatement(0).asExpressionStmt().getExpression().asMethodCallExpr().getNameAsString().equals("printStackTrace"));
396                                            }
397                                        }
398                                        return false;
399                                    })
400                                )) {
401                                foundInsecurePattern = true; // Also consider this an insecure pattern for patching
402                            }
403                        }
404                    }
405                }
406
407                if (foundInsecurePattern) {
408                    logPatch(line, "Insecure TrustManager replaced with secure placeholder requiring manual review.");
409                    // Replace the entire anonymous class with a secure placeholder
410                    oce.replace(StaticJavaParser.parseExpression("new javax.net.ssl.X509TrustManager() {\n" +
411                        "    @Override\n" +
412                        "    public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new java.security.cert.X509Certificate[0]; }\n" +
413                        "    @Override\n" +
414                        "    public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException {\n" +
415                        "        // AUTO-PATCH: Manual review required. Implement strict certificate validation here.\n" +
416                        "        throw new java.security.cert.CertificateException(\"Insecure TrustManager automatically patched: Manual review required.\");\n" +
417                        "    }\n" +
418                        "    @Override\n" +
419                        "    public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException {\n" +
420                        "        // AUTO-PATCH: Manual review required. Implement strict certificate validation here.\n" +
421                        "        throw new java.security.cert.CertificateException(\"Insecure TrustManager automatically patched: Manual review required.\");\n" +
422                        "    }\n" +
423                        "}"));
424                }
425            }
426
427            // NEW PATCH: Hardcoded Cryptographic Keys/Salts/IVs - Add Warning Comment
428            if (oce.getType().getNameAsString().equals("SecretKeySpec") ||
429                oce.getType().getNameAsString().equals("IvParameterSpec")) {
430                boolean hasLiteralArgument = false;
431                for (Expression argExpr : oce.getArguments()) {
432                    if (argExpr.isStringLiteralExpr()) {
433                        String value = argExpr.asStringLiteralExpr().getValue();
434                        if (value.length() > 5 && value.toLowerCase().matches(SENSITIVE_STRING_PATTERN)) {
435                            hasLiteralArgument = true;
436                            break;
437                        }
438                    } else if (argExpr.isMethodCallExpr() && argExpr.asMethodCallExpr().getScope().isPresent() && argExpr.asMethodCallExpr().getScope().get().isStringLiteralExpr()) {
439                        String value = argExpr.asMethodCallExpr().getScope().get().asStringLiteralExpr().getValue();
440                        if (value.length() > 5 && value.toLowerCase().matches(SENSITIVE_STRING_PATTERN)) {
441                            hasLiteralArgument = true;
442                            break;
443                        }
444                    } else if (argExpr.isArrayCreationExpr()) {
445                        ArrayCreationExpr ace = argExpr.asArrayCreationExpr();
446                        if (ace.getInitializer().isPresent() && ace.getInitializer().get().getValues().isNonEmpty()) {
447                            hasLiteralArgument = true; // Assume any direct array literal initialization is suspicious
448                            break;
449                        }
450                    }
451                }
452                if (hasLiteralArgument) {
453                    oce.getParentNode().ifPresent(parent -> {
454                        if (parent instanceof ExpressionStmt || parent instanceof VariableDeclarator) {
455                            String comment = "/* AUTO-PATCH: WARNING! This cryptographic key/salt/IV may be hardcoded.\n" +
456                                             " * Storing sensitive keys directly in code is a severe security risk.\n" +
457                                             " * Externalize this key/salt to a secure location (e.g., environment variable, KeyVault).\n" +
458                                             " */";
459                            parent.setComment(new BlockComment(comment));
460                            logPatch(line, "Added warning comment for potentially hardcoded cryptographic key/salt/IV.");
461                        }
462                    });
463                }
464            }
465
466            // NEW PATCH: Deserialization of Untrusted Data - Add Warning Comment
467            if (oce.getType().getNameAsString().equals("ObjectInputStream")) {
468                oce.getParentNode().ifPresent(parent -> {
469                    if (parent instanceof ExpressionStmt) {
470                        ExpressionStmt stmt = (ExpressionStmt) parent;
471                        String comment = "/* AUTO-PATCH: WARNING! ObjectInputStream is used here.\n" +
472                                         " * Deserializing untrusted data from an ObjectInputStream is a MAJOR security vulnerability (RCE).\n" +
473                                         " * Avoid using ObjectInputStream with untrusted sources. Consider safer formats like JSON/XML (with XXE protection).\n" +
474                                         " * If unavoidable, implement a robust deserialization filter (Java 9+).\n" +
475                                         " */";
476                        stmt.setComment(new BlockComment(comment));
477                        logPatch(line, "Added warning comment for ObjectInputStream (deserialization vulnerability).");
478                    }
479                });
480            }
481
482            return oce;
483        }
484
485        /**
486         * Visits VariableDeclarator nodes to apply patches related to variable declarations.
487         * This includes hardcoded sensitive data and unseeded SecureRandom instances.
488         *
489         * @param vd The VariableDeclarator node being visited.
490         * @param arg A generic argument (not used here).
491         * @return The modified VariableDeclarator.
492         */
493        @Override
494        public VariableDeclarator visit(VariableDeclarator vd, Void arg) {
495            super.visit(vd, arg); // Call super to ensure full traversal
496
497            int line = vd.getBegin().map(p -> p.line).orElse(-1);
498
499            // Patch: Unseeded SecureRandom instance during variable declaration
500            if (vd.getType() instanceof ClassOrInterfaceType) {
501                ClassOrInterfaceType classType = (ClassOrInterfaceType) vd.getType();
502                if (classType.getNameAsString().equals("SecureRandom") &&
503                    vd.getInitializer().isPresent() &&
504                    vd.getInitializer().get().isObjectCreationExpr()) {
505                    ObjectCreationExpr oce = vd.getInitializer().get().asObjectCreationExpr();
506                    if (oce.getType().getNameAsString().equals("SecureRandom") && oce.getArguments().isEmpty()) {
507                        logPatch(line, "Unseeded SecureRandom replaced with SecureRandom.getInstanceStrong() for variable: " + vd.getNameAsString());
508                        vd.setInitializer(StaticJavaParser.parseExpression("SecureRandom.getInstanceStrong()"));
509                    }
510                }
511            }
512
513            // Patch: Hardcoded password/sensitive string assigned to variable
514            if (vd.getInitializer().isPresent() && vd.getInitializer().get().isStringLiteralExpr()) {
515                String val = vd.getInitializer().get().asStringLiteralExpr().getValue();
516                // Using regex for more flexible pattern matching, and a minimum length to avoid false positives
517                if (val.matches(SENSITIVE_STRING_PATTERN) && val.length() > 3) { // Check against new pattern
518                    logPatch(line, "Hardcoded sensitive string assigned to variable '" + vd.getNameAsString() + "' replaced with environment lookup.");
519                    // Replace with environment variable lookup. Use StandardCharsets for robustness.
520                    vd.setInitializer(StaticJavaParser.parseExpression("System.getenv(\"" + vd.getNameAsString().toUpperCase() + "_SECRET\")"));
521                }
522            }
523
524            // Patch: Weak cipher suites in array initialization (already present)
525            if (vd.getType().isArrayType() &&
526                vd.getType().asArrayType().getComponentType().toString().equals("String") &&
527                vd.getInitializer().isPresent() &&
528                vd.getInitializer().get() instanceof ArrayInitializerExpr) {
529
530                ArrayInitializerExpr init = (ArrayInitializerExpr) vd.getInitializer().get();
531                
532                boolean weak = init.getValues().stream()
533                        .filter(Expression::isStringLiteralExpr)
534                        .map(expr -> expr.asStringLiteralExpr().getValue().toLowerCase())
535                        .anyMatch(val -> WEAK_CIPHER_KEYWORDS.stream().anyMatch(val::contains));
536
537                if (weak) {
538                    logPatch(line, "Weak cipher suites array replaced with strong defaults.");
539                    vd.setInitializer(StaticJavaParser.parseExpression(STRONG_CIPHERS_ARRAY_EXPR));
540                }
541            }
542
543            return vd;
544        }
545
546        /**
547         * Visits WhileStmt nodes to add a warning for potential infinite loops.
548         * Direct patching is too risky without deeper semantic analysis.
549         *
550         * @param ws The WhileStmt node being visited.
551         * @param arg A generic argument (not used here).
552         * @return The modified WhileStmt.
553         */
554        @Override
555        public WhileStmt visit(WhileStmt ws, Void arg) {
556            super.visit(ws, arg);
557            int line = ws.getBegin().map(p -> p.line).orElse(-1);
558
559            if (ws.getCondition().isBooleanLiteralExpr() &&
560                ws.getCondition().asBooleanLiteralExpr().getValue()) {
561                ws.setComment(new BlockComment("/* AUTO-PATCH: WARNING! Infinite loop (while(true)) detected.\n" +
562                                               " * This could be a Denial-of-Service vulnerability if it ties up resources.\n" +
563                                               " * Review the loop condition to ensure it terminates properly or has safeguards.\n" +
564                                               " * Manual review is required to determine if this is intentional or a vulnerability.\n" +
565                                               " */"));
566                logPatch(line, "Added warning comment for potential infinite loop (while(true)).");
567            }
568            return ws;
569        }
570
571        /**
572         * Visits TryStmt nodes to add a warning for overly broad exception catching.
573         * Direct patching of catch blocks is complex and risky.
574         *
575         * @param ts The TryStmt node being visited.
576         * @param arg A generic argument (not used here).
577         * @return The modified TryStmt.
578         */
579        @Override
580        public TryStmt visit(TryStmt ts, Void arg) {
581            super.visit(ts, arg);
582            int line = ts.getBegin().map(p -> p.line).orElse(-1);
583
584            ts.getCatchClauses().forEach(catchClause -> {
585                Type caughtType = catchClause.getParameter().getType();
586                if (caughtType instanceof ClassOrInterfaceType) {
587                    String typeName = ((ClassOrInterfaceType) caughtType).getNameAsString();
588                    if (typeName.equals("Exception") || typeName.equals("Throwable")) {
589                        if (catchClause.getBody().getStatements().isEmpty() ||
590                            (catchClause.getBody().getStatements().size() == 1 &&
591                             catchClause.getBody().getStatement(0).isExpressionStmt() &&
592                             catchClause.getBody().getStatement(0).asExpressionStmt().getExpression().isMethodCallExpr() &&
593                             catchClause.getBody().getStatement(0).asExpressionStmt().getExpression().asMethodCallExpr().getNameAsString().equals("printStackTrace"))) {
594                            String comment = "/* AUTO-PATCH: WARNING! Overly broad catch for '" + typeName + "' with minimal error handling.\n" +
595                                             " * This may hide critical exceptions, including security-related ones. Catch more specific exceptions.\n" +
596                                             " * Manual review is required to refine exception handling.\n" +
597                                             " */";
598                            catchClause.setComment(new BlockComment(comment));
599                            logPatch(line, "Added warning comment for overly broad catch block (" + typeName + ").");
600                        }
601                    }
602                }
603            });
604            return ts;
605        }
606    }
607}
608