🔒 AISAST Security Report

Project: /Users/jyothi/Projects/sast-webapp-package/uploads/tmp_eykhtyh
Generated: 2025-12-08 18:45:40
Files Scanned
33
Lines of Code
0
True Positives
20
Critical
3
High
10
Medium
6
Low
1

📚 Findings by CWE

CWE-1104 (1 findings)

  • Vulnerable Dependencies with Known Security Issues — pom.xml:35

CWE-319 (2 findings)

  • Mixed Content Security Risk - HTTP Resource Loading — client/index.html:54
  • Mixed Content Security Risk - HTTP Resource Loading in Login Page — client/login.html:39

CWE-327 (1 findings)

  • Weak Cryptographic Hash Algorithm (MD5) for Password Storage — src/main/java/com/scalesec/vulnado/Postgres.java:46

CWE-532 (1 findings)

  • Information Disclosure - System Information Leakage via Console Output — src/main/java/com/scalesec/vulnado/LinkLister.java:25

CWE-639 (1 findings)

  • Insecure Direct Object Reference (IDOR) in Comment Deletion — client/js/index.js:17

CWE-78 (1 findings)

  • Command Injection via ProcessBuilder with shell execution — src/main/java/com/scalesec/vulnado/Cowsay.java:9

CWE-79 (2 findings)

  • Cross-Site Scripting (XSS) via Handlebars Template Injection — client/js/index.js:32
  • Cross-Site Scripting (XSS) via Handlebars Triple-Brace Rendering — client/index.html:67

CWE-798 (3 findings)

  • Hardcoded Secret in Configuration File — src/main/resources/application.properties:1
  • Hardcoded Database Credentials in Docker Configuration — docker-compose.yml:10
  • Hardcoded Database Credentials in PostgreSQL Service — docker-compose.yml:25

CWE-862 (2 findings)

  • Missing Authentication in Comment Creation Endpoint — src/main/java/com/scalesec/vulnado/CommentsController.java:19
  • Missing Authentication in Comment Deletion Endpoint — src/main/java/com/scalesec/vulnado/CommentsController.java:25

CWE-89 (1 findings)

  • SQL Injection via String Concatenation in User Authentication — src/main/java/com/scalesec/vulnado/User.java:42

CWE-918 (2 findings)

  • Server-Side Request Forgery (SSRF) - Unrestricted URL Access — src/main/java/com/scalesec/vulnado/LinkLister.java:14
  • Server-Side Request Forgery (SSRF) - Incomplete Private IP Validation Bypass — src/main/java/com/scalesec/vulnado/LinkLister.java:14

CWE-922 (2 findings)

  • Sensitive Data Exposure via Local Storage — client/js/index.js:7
  • Insecure JWT Storage in Local Storage — client/js/login.js:17

CWE-942 (1 findings)

  • Cross-Origin Resource Sharing (CORS) Wildcard Misconfiguration — src/main/java/com/scalesec/vulnado/CommentsController.java:12
CRITICAL CWE-78 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 7408fe775bae

Command Injection via ProcessBuilder with shell execution

Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')

📍 Cowsay.java : Line 9
Full Path: src/main/java/com/scalesec/vulnado/Cowsay.java
Sink Method: com.scalesec.vulnado.Cowsay.run

⚠️ Vulnerable Code

processBuilder.command("bash", "-c", cmd);

📄 Surrounding Code Context (±5 lines)

5:   public static String run(String input) {
6:     ProcessBuilder processBuilder = new ProcessBuilder();
7:     String cmd = "/usr/games/cowsay '" + input + "'";
8:     System.out.println(cmd);
9:     processBuilder.command("bash", "-c", cmd);
10: 
11:     StringBuilder output = new StringBuilder();
12: 
13:     try {
14:       Process process = processBuilder.start();
15:       BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));

💡 Explanation

An attacker can execute arbitrary system commands on the server by injecting shell metacharacters into the input parameter. The single quotes around the input can be escaped using techniques like '; malicious_command; # or ' && malicious_command && '. This allows complete system compromise including data exfiltration, privilege escalation, lateral movement, and denial of service attacks.

🔗 Tainted Variables

inputcmd

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] src/main/java/com/scalesec/vulnado/CowController.java:11 (HTTP parameter)
    String cowsay(@RequestParam(defaultValue = "I love Linux!") String input) {input
  2. Step 2 [PROPAGATION] src/main/java/com/scalesec/vulnado/CowController.java:12
    return Cowsay.run(input);input
  3. Step 3 [PROPAGATION] src/main/java/com/scalesec/vulnado/Cowsay.java:7
    String cmd = "/usr/games/cowsay '" + input + "'";;cmd
  4. Step 4 [SINK] src/main/java/com/scalesec/vulnado/Cowsay.java:9
    processBuilder.command("bash", "-c", cmd);cmd

✅ Recommended Fix

1. Use ProcessBuilder with separate arguments instead of shell execution 2. Implement strict input validation and sanitization 3. Use allowlist validation for acceptable characters 4. Consider using safer alternatives that don't involve system command execution

public static String run(String input) {
    // Input validation - only allow alphanumeric, spaces, and basic punctuation
    if (!input.matches("^[a-zA-Z0-9\\s.,!?-]+$")) {
        throw new IllegalArgumentException("Invalid input characters");
    }
    
    // Use ProcessBuilder with separate arguments (no shell)
    ProcessBuilder processBuilder = new ProcessBuilder("/usr/games/cowsay", input);
    
    StringBuilder output = new StringBuilder();
    try {
        Process process = processBuilder.start();
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        
        String line;
        while ((line = reader.readLine()) != null) {
            output.append(line).append("\n");
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return output.toString();
}

📚 References

🏷️ Tags

command-injectionrceprocess-executionshell-injection
CRITICAL CWE-918 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 06b096966a82

Server-Side Request Forgery (SSRF) - Unrestricted URL Access

Server-Side Request Forgery (SSRF)

📍 LinkLister.java : Line 14
Full Path: src/main/java/com/scalesec/vulnado/LinkLister.java
Sink Method: com.scalesec.vulnado.LinkLister.getLinks

⚠️ Vulnerable Code

Document doc = Jsoup.connect(url).get();

📄 Surrounding Code Context (±5 lines)

11:   public static List<String> getLinks(String url) throws IOException {
12:     List<String> result = new ArrayList<String>();
13:     Document doc = Jsoup.connect(url).get();
14:     Elements links = doc.select("a");
15:     for (Element link : links) {
16:       result.add(link.absUrl("href"));
17:     }
18:     return result;
19:   }
20: 
21:   public static List<String> getLinksV2(String url) throws BadRequest {

💡 Explanation

An attacker can force the server to make HTTP requests to arbitrary URLs, including internal services, localhost, and private network resources. This can lead to: 1) Access to internal services and APIs not exposed to the internet, 2) Port scanning of internal networks, 3) Reading local files via file:// protocol, 4) Potential remote code execution if internal services have vulnerabilities, 5) Bypassing firewalls and network segmentation, 6) Information disclosure from internal systems.

🔗 Tainted Variables

url

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] src/main/java/com/scalesec/vulnado/LinksController.java:13 (HTTP parameter)
    List<String> links(@RequestParam String url) throws IOException{url
  2. Step 2 [PROPAGATION] src/main/java/com/scalesec/vulnado/LinksController.java:14
    return LinkLister.getLinks(url);url
  3. Step 3 [SINK] src/main/java/com/scalesec/vulnado/LinkLister.java:14
    Document doc = Jsoup.connect(url).get();url

✅ Recommended Fix

1) Implement URL validation with an allowlist of permitted domains/protocols, 2) Block private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8), 3) Only allow HTTP/HTTPS protocols, 4) Use DNS resolution validation to prevent DNS rebinding attacks, 5) Implement request timeouts and size limits, 6) Consider using a proxy service for external requests.

public static List<String> getLinks(String url) throws IOException, BadRequest {
    // Validate URL format
    URL validatedUrl;
    try {
        validatedUrl = new URL(url);
    } catch (MalformedURLException e) {
        throw new BadRequest("Invalid URL format");
    }
    
    // Only allow HTTP/HTTPS
    String protocol = validatedUrl.getProtocol().toLowerCase();
    if (!"http".equals(protocol) && !"https".equals(protocol)) {
        throw new BadRequest("Only HTTP/HTTPS protocols allowed");
    }
    
    // Block private IP ranges
    String host = validatedUrl.getHost();
    if (isPrivateIP(host)) {
        throw new BadRequest("Access to private IP ranges not allowed");
    }
    
    // Allowlist of permitted domains
    List<String> allowedDomains = Arrays.asList("example.com", "trusted-site.com");
    if (!allowedDomains.contains(host)) {
        throw new BadRequest("Domain not in allowlist");
    }
    
    List<String> result = new ArrayList<String>();
    Document doc = Jsoup.connect(url)
        .timeout(5000)
        .maxBodySize(1024 * 1024) // 1MB limit
        .get();
    Elements links = doc.select("a");
    for (Element link : links) {
        result.add(link.absUrl("href"));
    }
    return result;
}

private static boolean isPrivateIP(String host) {
    return host.startsWith("127.") || host.startsWith("10.") || 
           host.startsWith("192.168.") || host.startsWith("172.") ||
           "localhost".equals(host);
}

🏷️ Tags

ssrfnetworkinternal-accessjsoup
CRITICAL CWE-89 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 1bc131b18696

SQL Injection via String Concatenation in User Authentication

Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')

📍 User.java : Line 42
Full Path: src/main/java/com/scalesec/vulnado/User.java
Sink Method: com.scalesec.vulnado.User.fetch

⚠️ Vulnerable Code

String query = "select * from users where username = '" + un + "' limit 1";

📄 Surrounding Code Context (±5 lines)

37:       Connection cxn = Postgres.connection();
38:       stmt = cxn.createStatement();
39:       System.out.println("Opened database successfully");
40: 
41:       String query = "select * from users where username = '" + un + "' limit 1";
42:       System.out.println(query);
43:       ResultSet rs = stmt.executeQuery(query);
44:       if (rs.next()) {
45:         String user_id = rs.getString("user_id");
46:         String username = rs.getString("username");
47:         String password = rs.getString("password");

💡 Explanation

An attacker can inject malicious SQL code through the username parameter in login requests. This allows complete database compromise including: extracting all user credentials, bypassing authentication, modifying/deleting data, and potentially executing system commands depending on database permissions. The vulnerability enables authentication bypass and complete application takeover.

🔗 Tainted Variables

input.usernameunquery

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] src/main/java/com/scalesec/vulnado/LoginController.java:18 (HTTP request body)
    User user = User.fetch(input.username);input.username
  2. Step 2 [PROPAGATION] src/main/java/com/scalesec/vulnado/User.java:33
    public static User fetch(String un) {un
  3. Step 3 [SINK] src/main/java/com/scalesec/vulnado/User.java:42
    String query = "select * from users where username = '" + un + "' limit 1";un

✅ Recommended Fix

Replace string concatenation with parameterized queries using PreparedStatement to prevent SQL injection attacks.

public static User fetch(String un) {
  PreparedStatement pstmt = null;
  User user = null;
  try {
    Connection cxn = Postgres.connection();
    String query = "select * from users where username = ? limit 1";
    pstmt = cxn.prepareStatement(query);
    pstmt.setString(1, un);
    ResultSet rs = pstmt.executeQuery();
    if (rs.next()) {
      String user_id = rs.getString("user_id");
      String username = rs.getString("username");
      String password = rs.getString("password");
      user = new User(user_id, username, password);
    }
    cxn.close();
  } catch (Exception e) {
    e.printStackTrace();
  } finally {
    return user;
  }
}

📚 References

🏷️ Tags

sql-injectionauthentication-bypassdatabasecritical
HIGH CWE-79 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 0e1a3818fcb5

Cross-Site Scripting (XSS) via Handlebars Template Injection

Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

📍 index.js : Line 32
Full Path: client/js/index.js
Sink Method: fetchComments

⚠️ Vulnerable Code

$("#comments-container").append(template(comment));

📄 Surrounding Code Context (±5 lines)

27:   function fetchComments() {
28:     $.get("http://localhost:8080/comments", function(data){
29:       $('#comments-container').html('')
30:       data.forEach(function(comment){
31:         if (comment.body.indexOf("<script>") < 0) {
32:           $("#comments-container").append(template(comment));
33:         }
34:       });
35:       setupDeleteCommentHandler();
36:     });
37:   }

💡 Explanation

An attacker can inject malicious JavaScript code through comment data that bypasses the weak XSS filter. The filter only checks for '<script>' tags but Handlebars templates can execute JavaScript through various other vectors like {{constructor.constructor('alert(1)')()}} or event handlers in HTML attributes. This allows for session hijacking, credential theft, defacement, and arbitrary actions on behalf of users.

🔗 Tainted Variables

datacomment

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] client/js/index.js:28 (HTTP response)
    $.get("http://localhost:8080/comments", function(data){data
  2. Step 2 [PROPAGATION] client/js/index.js:30
    data.forEach(function(comment){comment
  3. Step 3 [SINK] client/js/index.js:32
    $("#comments-container").append(template(comment));comment

✅ Recommended Fix

1. Use Handlebars' built-in HTML escaping by default (triple braces {{{ }}} should be avoided). 2. Implement proper server-side input validation and output encoding. 3. Use Content Security Policy (CSP) headers. 4. Replace the weak indexOf filter with a proper HTML sanitization library like DOMPurify.

// Use DOMPurify for proper sanitization
function fetchComments() {
  $.get("http://localhost:8080/comments", function(data){
    $('#comments-container').html('')
    data.forEach(function(comment){
      // Sanitize the comment body before templating
      comment.body = DOMPurify.sanitize(comment.body);
      $("#comments-container").append(template(comment));
    });
    setupDeleteCommentHandler();
  });
}

📚 References

🏷️ Tags

xsstemplate-injectionclient-sidehandlebars
HIGH CWE-918 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 08ed39ec7a32

Server-Side Request Forgery (SSRF) - Incomplete Private IP Validation Bypass

Server-Side Request Forgery (SSRF)

📍 LinkLister.java : Line 14
Full Path: src/main/java/com/scalesec/vulnado/LinkLister.java
Sink Method: com.scalesec.vulnado.LinkLister.getLinksV2

⚠️ Vulnerable Code

Document doc = Jsoup.connect(url).get();

📄 Surrounding Code Context (±5 lines)

21:   public static List<String> getLinksV2(String url) throws BadRequest {
22:     try {
23:       URL aUrl= new URL(url);
24:       String host = aUrl.getHost();
25:       System.out.println(host);
26:       if (host.startsWith("172.") || host.startsWith("192.168") || host.startsWith("10.")){
27:         throw new BadRequest("Use of Private IP");
28:       } else {
29:         return getLinks(url);
30:       }
31:     } catch(Exception e) {
32:       throw new BadRequest(e.getMessage());
33:     }

💡 Explanation

The private IP validation can be bypassed through multiple attack vectors: 1) Localhost access via 127.0.0.1, ::1, or 'localhost', 2) Link-local addresses (169.254.x.x), 3) DNS rebinding attacks using malicious domains that resolve to private IPs, 4) URL encoding or alternative IP representations (hex, octal, integer format), 5) IPv6 private ranges, 6) Non-HTTP protocols like file://, ftp://, gopher://. Attackers can still access internal services and perform SSRF attacks.

🔗 Tainted Variables

urlhost

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] src/main/java/com/scalesec/vulnado/LinksController.java:17 (HTTP parameter)
    List<String> linksV2(@RequestParam String url) throws BadRequest{url
  2. Step 2 [PROPAGATION] src/main/java/com/scalesec/vulnado/LinksController.java:18
    return LinkLister.getLinksV2(url);url
  3. Step 3 [PROPAGATION] src/main/java/com/scalesec/vulnado/LinkLister.java:29
    return getLinks(url);url
  4. Step 4 [SINK] src/main/java/com/scalesec/vulnado/LinkLister.java:14
    Document doc = Jsoup.connect(url).get();url

✅ Recommended Fix

1) Implement comprehensive IP range validation including localhost (127.0.0.0/8), link-local (169.254.0.0/16), and IPv6 private ranges, 2) Resolve hostnames to IP addresses and validate the resolved IPs, 3) Block non-HTTP/HTTPS protocols, 4) Use proper IP address parsing libraries instead of string matching, 5) Implement DNS resolution validation to prevent rebinding attacks, 6) Consider using a dedicated service or proxy for external requests.

public static List<String> getLinksV2(String url) throws BadRequest {
    try {
        URL aUrl = new URL(url);
        String protocol = aUrl.getProtocol().toLowerCase();
        
        // Only allow HTTP/HTTPS
        if (!"http".equals(protocol) && !"https".equals(protocol)) {
            throw new BadRequest("Only HTTP/HTTPS protocols allowed");
        }
        
        String host = aUrl.getHost();
        
        // Resolve hostname to IP and validate
        InetAddress[] addresses = InetAddress.getAllByName(host);
        for (InetAddress addr : addresses) {
            if (isPrivateOrLocalAddress(addr)) {
                throw new BadRequest("Access to private/local IP ranges not allowed");
            }
        }
        
        return getLinks(url);
    } catch (UnknownHostException e) {
        throw new BadRequest("Unable to resolve hostname");
    } catch (Exception e) {
        throw new BadRequest("Invalid URL: " + e.getMessage());
    }
}

private static boolean isPrivateOrLocalAddress(InetAddress addr) {
    return addr.isLoopbackAddress() || 
           addr.isLinkLocalAddress() || 
           addr.isSiteLocalAddress() || 
           addr.isAnyLocalAddress();
}

🏷️ Tags

ssrfvalidation-bypassprivate-ipincomplete-fix
HIGH CWE-1104 HIGH ✓ VERIFIED TRUE POSITIVE
ID: e83edd1d4eea

Vulnerable Dependencies with Known Security Issues

Use of Unmaintained Third Party Components

📍 pom.xml : Line 35
Full Path: pom.xml
Sink Method: N/A

⚠️ Vulnerable Code

<version>1.8.3</version>

📄 Surrounding Code Context (±5 lines)

33: 		<dependency>
34: 		    <groupId>org.jsoup</groupId>
35: 		    <artifactId>jsoup</artifactId>
36: 		    <version>1.8.3</version>
37: 		</dependency>

💡 Explanation

The application uses JSoup version 1.8.3, which is significantly outdated (released in 2015) and contains multiple known security vulnerabilities including CVE-2021-37714 (XSS bypass), CVE-2022-36033 (XSS bypass), and other parsing vulnerabilities. These vulnerabilities can allow attackers to bypass HTML sanitization, leading to Cross-Site Scripting attacks when processing untrusted HTML content.

🔗 Tainted Variables

jsoup

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] pom.xml:35 (dependency declaration)
    <version>1.8.3</version>jsoup.version
  2. Step 2 [SINK] pom.xml:35
    <version>1.8.3</version>jsoup.version

✅ Recommended Fix

Update JSoup to the latest stable version (1.15.3 or newer) to address known security vulnerabilities. Review all usage of JSoup in the codebase to ensure proper sanitization practices.

<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.15.3</version>
</dependency>

📚 References

🏷️ Tags

dependencyoutdated-libraryjsoupxss-bypass
HIGH CWE-798 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 6d2808fe076e

Hardcoded Secret in Configuration File

Use of Hard-coded Credentials

📍 application.properties : Line 1
Full Path: src/main/resources/application.properties
Sink Method: N/A

⚠️ Vulnerable Code

app.secret=edf10572-880c-4dd9-aaf0-6ec402f678db

📄 Surrounding Code Context (±5 lines)

1: app.secret=edf10572-880c-4dd9-aaf0-6ec402f678db

💡 Explanation

The hardcoded secret 'edf10572-880c-4dd9-aaf0-6ec402f678db' is exposed in the application configuration file. This secret appears to be a UUID that could be used for JWT signing, encryption keys, or other security-critical operations. Attackers with access to the source code, configuration files, or deployed application can extract this secret and potentially forge tokens, decrypt sensitive data, or bypass authentication mechanisms.

🔗 Tainted Variables

app.secret

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] src/main/resources/application.properties:1 (hardcoded configuration)
    app.secret=edf10572-880c-4dd9-aaf0-6ec402f678dbapp.secret
  2. Step 2 [SINK] src/main/resources/application.properties:1
    app.secret=edf10572-880c-4dd9-aaf0-6ec402f678dbapp.secret

✅ Recommended Fix

Remove the hardcoded secret from the configuration file and use environment variables or a secure secret management system. Configure the application to read secrets from environment variables at runtime.

# Remove hardcoded secret
# app.secret=edf10572-880c-4dd9-aaf0-6ec402f678db

# Use environment variable instead
app.secret=${APP_SECRET:}

# Or use Spring Boot's encrypted properties
# app.secret={cipher}AQA...

📚 References

🏷️ Tags

secretsconfigurationhardcoded-credentialsspring-boot
HIGH CWE-79 HIGH ✓ VERIFIED TRUE POSITIVE
ID: efa741d11f8e

Cross-Site Scripting (XSS) via Handlebars Triple-Brace Rendering

Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

📍 index.html : Line 67
Full Path: client/index.html
Sink Method: handlebars.template.render

⚠️ Vulnerable Code

<p class="card-text">{{{body}}}</p>

📄 Surrounding Code Context (±5 lines)

62:             <div class="card-header">
63:               <strong>{{username}}</strong> commented at {{created_on}}
64:               <span class="delete-comment">
65:                 <img src="images/trash.png" height=15px/>
66:               </span>
67:             </div>
68:             <div class="card-body">
69:               <p class="card-text">{{{body}}}</p>
70:             </div>
71:           </div>
72:         </div><!-- /col-sm-12 -->

💡 Explanation

Attackers can inject arbitrary JavaScript code into comment bodies that will execute in other users' browsers when they view the comments. This enables session hijacking, credential theft, defacement, malware distribution, and complete compromise of user accounts. The triple-brace syntax {{{body}}} in Handlebars bypasses HTML escaping, making this a stored XSS vulnerability affecting all users who view the malicious comment.

🔗 Tainted Variables

bodycomment_body

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] client/index.html:44 (user input)
    <input type="text" id="new-comment" class="form-control" placeholder="Comment">comment_body
  2. Step 2 [PROPAGATION] client/js/index.js
    comment submission via AJAXcomment_body
  3. Step 3 [SINK] client/index.html:67
    <p class="card-text">{{{body}}}</p>body

✅ Recommended Fix

Replace triple-brace {{{body}}} with double-brace {{body}} to enable HTML escaping, implement server-side input validation and sanitization, use Content Security Policy headers, and validate/sanitize data on both client and server sides.

<!-- Use double braces for HTML escaping -->
<p class="card-text">{{body}}</p>

<!-- Add CSP header -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' https://code.jquery.com https://cdnjs.cloudflare.com https://maxcdn.bootstrapcdn.com; style-src 'self' 'unsafe-inline' https://maxcdn.bootstrapcdn.com;">

📚 References

🏷️ Tags

xssstored-xsshandlebarsclient-sideinjection
HIGH CWE-798 HIGH ✓ VERIFIED TRUE POSITIVE
ID: a6f0d41d5cbc

Hardcoded Database Credentials in Docker Configuration

Use of Hard-coded Credentials

📍 docker-compose.yml : Line 10
Full Path: docker-compose.yml
Sink Method: environment.variable

⚠️ Vulnerable Code

- PGPASSWORD=vulnado

📄 Surrounding Code Context (±5 lines)

5:     ports:
6:       - 8080:8080
7:     links:
8:       - db
9:       - internal_site
10:     environment:
11:       - PGPASSWORD=vulnado
12:       - PGDATABASE=vulnado
13:       - PGHOST=db:5432
14:       - PGUSER=postgres
15:     depends_on:

💡 Explanation

Hardcoded database credentials in configuration files expose the database password to anyone with access to the source code repository. This enables unauthorized database access, data theft, data manipulation, and potential complete system compromise. The credentials are visible in version control history and deployment artifacts.

🔗 Tainted Variables

PGPASSWORD

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] docker-compose.yml:10 (hardcoded credential)
    - PGPASSWORD=vulnadoPGPASSWORD
  2. Step 2 [SINK] docker-compose.yml:10
    - PGPASSWORD=vulnadoPGPASSWORD

✅ Recommended Fix

Use environment variables, Docker secrets, or external secret management systems. Never commit credentials to version control. Use .env files that are excluded from git, or runtime secret injection.

# Use external environment variables or secrets
environment:
  - PGPASSWORD=${DB_PASSWORD} # Set externally
  - PGDATABASE=${DB_NAME}
  - PGHOST=db:5432
  - PGUSER=${DB_USER}

# Or use Docker secrets
secrets:
  - db_password

# In .env file (not committed to git):
# DB_PASSWORD=secure_random_password
# DB_NAME=vulnado
# DB_USER=postgres

📚 References

🏷️ Tags

hardcoded-credentialsdatabasedockersecrets-management
HIGH CWE-798 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 622fc0d92299

Hardcoded Database Credentials in PostgreSQL Service

Use of Hard-coded Credentials

📍 docker-compose.yml : Line 25
Full Path: docker-compose.yml
Sink Method: environment.variable

⚠️ Vulnerable Code

- POSTGRES_PASSWORD=vulnado

📄 Surrounding Code Context (±5 lines)

20:   db:
21:     image: postgres
22:     environment:
23:       - POSTGRES_PASSWORD=vulnado
24:       - POSTGRES_DB=vulnado
25: 
26:   internal_site:
27:     build: internal_site

💡 Explanation

Hardcoded PostgreSQL root password exposes the database to unauthorized access. Anyone with access to the source code can connect to the database with full administrative privileges, enabling data theft, data corruption, privilege escalation, and complete database compromise.

🔗 Tainted Variables

POSTGRES_PASSWORD

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] docker-compose.yml:25 (hardcoded credential)
    - POSTGRES_PASSWORD=vulnadoPOSTGRES_PASSWORD
  2. Step 2 [SINK] docker-compose.yml:25
    - POSTGRES_PASSWORD=vulnadoPOSTGRES_PASSWORD

✅ Recommended Fix

Use environment variables, Docker secrets, or external secret management. Generate strong random passwords and inject them at runtime rather than hardcoding in configuration files.

# Use external environment variables
db:
  image: postgres
  environment:
    - POSTGRES_PASSWORD=${POSTGRES_ROOT_PASSWORD}
    - POSTGRES_DB=${POSTGRES_DB_NAME}
  secrets:
    - postgres_password

# Define secrets
secrets:
  postgres_password:
    external: true

📚 References

🏷️ Tags

hardcoded-credentialspostgresqldatabasedocker-secrets
HIGH CWE-862 HIGH ✓ VERIFIED TRUE POSITIVE
ID: dc9a39a8f8ef

Missing Authentication in Comment Creation Endpoint

Missing Authorization

📍 CommentsController.java : Line 19
Full Path: src/main/java/com/scalesec/vulnado/CommentsController.java
Sink Method: com.scalesec.vulnado.CommentsController.createComment

⚠️ Vulnerable Code

Comment createComment(@RequestHeader(value="x-auth-token") String token, @RequestBody CommentRequest input) {

📄 Surrounding Code Context (±5 lines)

17:   @CrossOrigin(origins = "*")
18:   @RequestMapping(value = "/comments", method = RequestMethod.POST, produces = "application/json", consumes = "application/json")
19:   Comment createComment(@RequestHeader(value="x-auth-token") String token, @RequestBody CommentRequest input) {
20:     return Comment.create(input.username, input.body);
21:   }
22: 

💡 Explanation

The createComment endpoint accepts an authentication token but never validates it, allowing unauthenticated users to create comments with arbitrary usernames. Attackers can impersonate any user, spam the system with fake comments, and potentially conduct social engineering attacks by posting malicious content under trusted usernames.

🔗 Tainted Variables

tokeninput

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] src/main/java/com/scalesec/vulnado/CommentsController.java:19 (HTTP header)
    Comment createComment(@RequestHeader(value="x-auth-token") String token, @RequestBody CommentRequest input) {token
  2. Step 2 [SINK] src/main/java/com/scalesec/vulnado/CommentsController.java:20
    return Comment.create(input.username, input.body);input

✅ Recommended Fix

Add authentication validation by calling User.assertAuth() before processing the comment creation request.

@CrossOrigin(origins = "*")
@RequestMapping(value = "/comments", method = RequestMethod.POST, produces = "application/json", consumes = "application/json")
Comment createComment(@RequestHeader(value="x-auth-token") String token, @RequestBody CommentRequest input) {
  User.assertAuth(secret, token);
  return Comment.create(input.username, input.body);
}

📚 References

🏷️ Tags

authenticationauthorizationaccess-controlimpersonation
HIGH CWE-862 HIGH ✓ VERIFIED TRUE POSITIVE
ID: b3e22c4f3e78

Missing Authentication in Comment Deletion Endpoint

Missing Authorization

📍 CommentsController.java : Line 25
Full Path: src/main/java/com/scalesec/vulnado/CommentsController.java
Sink Method: com.scalesec.vulnado.CommentsController.deleteComment

⚠️ Vulnerable Code

Boolean deleteComment(@RequestHeader(value="x-auth-token") String token, @PathVariable("id") String id) {

📄 Surrounding Code Context (±5 lines)

23:   @CrossOrigin(origins = "*")
24:   @RequestMapping(value = "/comments/{id}", method = RequestMethod.DELETE, produces = "application/json")
25:   Boolean deleteComment(@RequestHeader(value="x-auth-token") String token, @PathVariable("id") String id) {
26:     return Comment.delete(id);
27:   }
28: }

💡 Explanation

The deleteComment endpoint accepts an authentication token but never validates it, allowing unauthenticated users to delete any comment by providing its ID. Attackers can perform denial of service attacks by deleting legitimate comments, conduct censorship attacks, and disrupt normal application functionality without any authentication.

🔗 Tainted Variables

tokenid

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] src/main/java/com/scalesec/vulnado/CommentsController.java:25 (HTTP header)
    Boolean deleteComment(@RequestHeader(value="x-auth-token") String token, @PathVariable("id") String id) {token
  2. Step 2 [SINK] src/main/java/com/scalesec/vulnado/CommentsController.java:26
    return Comment.delete(id);id

✅ Recommended Fix

Add authentication validation by calling User.assertAuth() before processing the comment deletion request.

@CrossOrigin(origins = "*")
@RequestMapping(value = "/comments/{id}", method = RequestMethod.DELETE, produces = "application/json")
Boolean deleteComment(@RequestHeader(value="x-auth-token") String token, @PathVariable("id") String id) {
  User.assertAuth(secret, token);
  return Comment.delete(id);
}

📚 References

🏷️ Tags

authenticationauthorizationaccess-controldata-deletion
HIGH CWE-327 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 42a406251df4

Weak Cryptographic Hash Algorithm (MD5) for Password Storage

Use of a Broken or Risky Cryptographic Algorithm

📍 Postgres.java : Line 46
Full Path: src/main/java/com/scalesec/vulnado/Postgres.java
Sink Method: com.scalesec.vulnado.Postgres.md5

⚠️ Vulnerable Code

MessageDigest md = MessageDigest.getInstance("MD5");

📄 Surrounding Code Context (±5 lines)

42:     public static String md5(String input)
43:     {
44:         try {
45: 
46:             // Static getInstance method is called with hashing MD5
47:             MessageDigest md = MessageDigest.getInstance("MD5");
48: 
49:             // digest() method is called to calculate message digest
50:             //  of an input digest() return array of byte
51:             byte[] messageDigest = md.digest(input.getBytes());
52: 

💡 Explanation

MD5 is cryptographically broken and vulnerable to collision attacks and rainbow table attacks. Attackers can easily crack MD5 hashes using precomputed tables or brute force attacks, especially for common passwords. This compromises all user passwords stored in the database and enables account takeover attacks.

🔗 Tainted Variables

input.passwordinput

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] src/main/java/com/scalesec/vulnado/LoginController.java:18 (HTTP request body)
    if (Postgres.md5(input.password).equals(user.hashedPassword)) {input.password
  2. Step 2 [PROPAGATION] src/main/java/com/scalesec/vulnado/Postgres.java:42
    public static String md5(String input)input
  3. Step 3 [SINK] src/main/java/com/scalesec/vulnado/Postgres.java:46
    MessageDigest md = MessageDigest.getInstance("MD5");input

✅ Recommended Fix

Replace MD5 with a strong password hashing algorithm like bcrypt, scrypt, or Argon2 that includes salt and is designed to be computationally expensive.

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public static String hashPassword(String password) {
    BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(12);
    return encoder.encode(password);
}

public static boolean verifyPassword(String password, String hashedPassword) {
    BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
    return encoder.matches(password, hashedPassword);
}

📚 References

🏷️ Tags

cryptographypassword-storagehash-algorithmauthentication
MEDIUM CWE-639 HIGH ✓ VERIFIED TRUE POSITIVE
ID: c9b659e2efb5

Insecure Direct Object Reference (IDOR) in Comment Deletion

Authorization Bypass Through User-Controlled Key

📍 index.js : Line 17
Full Path: client/js/index.js
Sink Method: setupDeleteCommentHandler

⚠️ Vulnerable Code

url: "http://localhost:8080/comments/" + id

📄 Surrounding Code Context (±5 lines)

12:     $('.delete-comment').click(function(){
13:       var parent = this.closest(".row");
14:       var id = $(parent).data("comment_id");
15: 
16:       $.ajax({
17:         type: "DELETE",
18:         url: "http://localhost:8080/comments/" + id
19:       }).done(function(){
20:         $(parent).remove();
21:       });
22:     });

💡 Explanation

An attacker can manipulate the comment_id data attribute in the DOM to delete any comment by changing the ID value. This allows unauthorized deletion of other users' comments, potentially leading to data loss and violation of data integrity. The client-side code trusts the DOM data without server-side authorization checks.

🔗 Tainted Variables

id

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] client/js/index.js:14 (DOM data attribute)
    var id = $(parent).data("comment_id");id
  2. Step 2 [SINK] client/js/index.js:17
    url: "http://localhost:8080/comments/" + idid

✅ Recommended Fix

1. Implement proper server-side authorization to verify the user owns the comment before deletion. 2. Use session-based authentication to validate user permissions. 3. Add CSRF protection. 4. Log deletion attempts for audit purposes.

// Client-side: Add CSRF token
$('.delete-comment').click(function(){
  var parent = this.closest(".row");
  var id = $(parent).data("comment_id");
  
  if (!confirm('Are you sure you want to delete this comment?')) {
    return;
  }
  
  $.ajax({
    type: "DELETE",
    url: "http://localhost:8080/comments/" + id,
    headers: {
      'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
    }
  }).done(function(){
    $(parent).remove();
  }).fail(function(xhr) {
    alert('Failed to delete comment: ' + xhr.responseText);
  });
});

🏷️ Tags

idorauthorizationaccess-controlclient-side
MEDIUM CWE-922 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 42a37c9ee904

Sensitive Data Exposure via Local Storage

Insecure Storage of Sensitive Information

📍 index.js : Line 7
Full Path: client/js/index.js
Sink Method: $.ajaxSetup

⚠️ Vulnerable Code

xhr.setRequestHeader('x-auth-token', localStorage.jwt);

📄 Surrounding Code Context (±5 lines)

2:   var source = $("#comment-template").html();
3:   var template = Handlebars.compile(source);
4: 
5:   // Add JWT to every request
6:   $.ajaxSetup({ beforeSend: function(xhr) {
7:     xhr.setRequestHeader('x-auth-token', localStorage.jwt);
8:   }});
9: 
10:   // Helper Functions
11:   function setupDeleteCommentHandler() {
12:     // NOTE: This needs to come first since comments aren't loaded yet.

💡 Explanation

JWT tokens and usernames stored in localStorage are accessible to any JavaScript code running on the same origin, including malicious scripts injected via XSS attacks. This data persists across browser sessions and can be accessed by browser extensions or local malware. An attacker gaining access to these tokens can impersonate users and perform unauthorized actions.

🔗 Tainted Variables

localStorage.jwtlocalStorage.username

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] client/js/index.js:7 (local storage)
    xhr.setRequestHeader('x-auth-token', localStorage.jwt);localStorage.jwt
  2. Step 2 [SINK] client/js/index.js:7
    xhr.setRequestHeader('x-auth-token', localStorage.jwt);localStorage.jwt

✅ Recommended Fix

1. Store JWT tokens in httpOnly, secure cookies instead of localStorage. 2. Implement proper session management with shorter token lifetimes. 3. Use secure, httpOnly cookies for authentication. 4. Implement token refresh mechanisms. 5. Clear sensitive data on logout.

// Remove localStorage usage for sensitive data
// Server should set httpOnly cookies instead
$.ajaxSetup({ 
  beforeSend: function(xhr) {
    // JWT will be sent automatically via httpOnly cookie
    // No need to manually set headers
  },
  xhrFields: {
    withCredentials: true // Include cookies in requests
  }
});

// Clear any existing localStorage data
$('#signout').click(function(){
  alert("Goodbye!");
  // Clear localStorage completely
  localStorage.clear();
  // Server should invalidate the session cookie
  $.post('/logout').always(function() {
    window.location.replace("login.html");
  });
});

📚 References

🏷️ Tags

sensitive-datalocal-storagejwtsession-management
MEDIUM CWE-319 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 3e2ec88f35f0

Mixed Content Security Risk - HTTP Resource Loading

Cleartext Transmission of Sensitive Information

📍 index.html : Line 54
Full Path: client/index.html
Sink Method: script.load

⚠️ Vulnerable Code

<script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars.min-v4.1.0.js"></script>

📄 Surrounding Code Context (±5 lines)

49:     <!-- Optional JavaScript -->
50:     <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
51:     <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
52:     <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
53:     <script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars.min-v4.1.0.js"></script>
54:     <script src="js/index.js"></script>
55:     <script id="comment-template" type="text/x-handlebars-template">
56:       <div class="row" data-comment_id="{{id}}">
57:         <div class="col-sm-12" style="padding:5px">
58:           <div class="card">
59:             <div class="card-header">

💡 Explanation

Loading JavaScript libraries over HTTP creates multiple security risks: Man-in-the-middle attacks can inject malicious code, mixed content warnings in browsers, potential for script tampering during transmission, and violation of security best practices. When served over HTTPS, this creates mixed content that browsers may block or warn about.

🔗 Tainted Variables

handlebars_script

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] client/index.html:54 (external HTTP resource)
    <script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars.min-v4.1.0.js"></script>handlebars_script
  2. Step 2 [SINK] client/index.html:54
    <script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars.min-v4.1.0.js"></script>handlebars_script

✅ Recommended Fix

Change HTTP URLs to HTTPS, add Subresource Integrity (SRI) hashes to verify script authenticity, and consider hosting critical libraries locally to reduce external dependencies.

<!-- Use HTTPS and add SRI hash -->
<script src="https://cdn.jsdelivr.net/npm/handlebars@4.1.0/dist/handlebars.min.js" integrity="sha384-[SRI-HASH]" crossorigin="anonymous"></script>

📚 References

🏷️ Tags

mixed-contenthttpexternal-resourcesmitm
MEDIUM CWE-922 HIGH ✓ VERIFIED TRUE POSITIVE
ID: c50f9344e640

Insecure JWT Storage in Local Storage

Insecure Storage of Sensitive Information

📍 login.js : Line 17
Full Path: client/js/login.js
Sink Method: localStorage.setItem

⚠️ Vulnerable Code

localStorage.jwt = data.token;

📄 Surrounding Code Context (±5 lines)

12:     })
13:     .fail(function(data){
14:       alert("Whoops, try again");
15:     })
16:     .done(function(data){
17:       localStorage.jwt = data.token;
18:       var username = JSON.parse(atob(data.token.split('.')[1]))['sub'];
19:       localStorage.username = username;
20:       window.location.replace("index.html");
21:     })
22:   })

💡 Explanation

Storing JWT tokens in localStorage makes them accessible to any JavaScript code running on the page, including malicious scripts injected via XSS attacks. Unlike httpOnly cookies, localStorage data persists across browser sessions and can be easily accessed by attackers, leading to session hijacking and unauthorized access to user accounts.

🔗 Tainted Variables

data.tokenlocalStorage.jwt

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] client/js/login.js:17 (HTTP response)
    localStorage.jwt = data.token;data.token
  2. Step 2 [SINK] client/js/login.js:17
    localStorage.jwt = data.token;localStorage.jwt

✅ Recommended Fix

Store JWT tokens in httpOnly, secure cookies instead of localStorage. Implement proper CSRF protection when using cookies, and consider using short-lived tokens with refresh token rotation.

// Server should set httpOnly cookie instead
// Remove localStorage usage
// .done(function(data){
//   // Token will be automatically stored in httpOnly cookie by server
//   var username = JSON.parse(atob(data.token.split('.')[1]))['sub'];
//   sessionStorage.username = username; // Only store non-sensitive data
//   window.location.replace("index.html");
// })

📚 References

🏷️ Tags

jwtlocalStoragesession-managementtoken-storage
MEDIUM CWE-319 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 9046310329d6

Mixed Content Security Risk - HTTP Resource Loading in Login Page

Cleartext Transmission of Sensitive Information

📍 login.html : Line 39
Full Path: client/login.html
Sink Method: script.load

⚠️ Vulnerable Code

<script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars.min-v4.1.0.js"></script>

📄 Surrounding Code Context (±5 lines)

34:   </body>
35:   <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
36:   <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
37:   <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
38:   <script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars.min-v4.1.0.js"></script>
39:   <script src="js/login.js"></script>
40: </html>

💡 Explanation

Loading JavaScript libraries over HTTP on the login page creates critical security risks: Man-in-the-middle attacks can inject credential-stealing code, compromise the authentication process, and potentially capture user login credentials. This is especially dangerous on a login page where sensitive authentication data is processed.

🔗 Tainted Variables

handlebars_script

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] client/login.html:39 (external HTTP resource)
    <script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars.min-v4.1.0.js"></script>handlebars_script
  2. Step 2 [SINK] client/login.html:39
    <script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars.min-v4.1.0.js"></script>handlebars_script

✅ Recommended Fix

Change HTTP URLs to HTTPS, add Subresource Integrity (SRI) hashes, and consider removing unused libraries from the login page to minimize attack surface.

<!-- Use HTTPS and add SRI hash, or remove if not needed -->
<script src="https://cdn.jsdelivr.net/npm/handlebars@4.1.0/dist/handlebars.min.js" integrity="sha384-[SRI-HASH]" crossorigin="anonymous"></script>

📚 References

🏷️ Tags

mixed-contenthttplogin-pageauthenticationmitm
MEDIUM CWE-942 HIGH ✓ VERIFIED TRUE POSITIVE
ID: ab46dd7f7cdf

Cross-Origin Resource Sharing (CORS) Wildcard Misconfiguration

Permissive Cross-domain Policy with Untrusted Domains

📍 CommentsController.java : Line 12
Full Path: src/main/java/com/scalesec/vulnado/CommentsController.java
Sink Method: com.scalesec.vulnado.CommentsController.comments

⚠️ Vulnerable Code

@CrossOrigin(origins = "*")

📄 Surrounding Code Context (±5 lines)

10:   private String secret;
11: 
12:   @CrossOrigin(origins = "*")
13:   @RequestMapping(value = "/comments", method = RequestMethod.GET, produces = "application/json")
14:   List<Comment> comments(@RequestHeader(value="x-auth-token") String token) {
15:     User.assertAuth(secret, token);
16:     return Comment.fetch_all();

💡 Explanation

The wildcard CORS policy allows any domain to make authenticated requests to the API endpoints. This enables malicious websites to perform cross-origin requests using victims' authentication tokens, potentially leading to data theft, unauthorized actions, and CSRF-like attacks from any external domain.

🔗 Tainted Variables

originstoken

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] src/main/java/com/scalesec/vulnado/CommentsController.java:12 (annotation configuration)
    @CrossOrigin(origins = "*")origins
  2. Step 2 [SINK] src/main/java/com/scalesec/vulnado/CommentsController.java:14
    List<Comment> comments(@RequestHeader(value="x-auth-token") String token) {token

✅ Recommended Fix

Replace wildcard CORS policy with specific trusted domains or implement proper CORS configuration with credentials handling.

@CrossOrigin(origins = {"https://trusted-domain.com", "https://app.example.com"}, allowCredentials = "true")
@RequestMapping(value = "/comments", method = RequestMethod.GET, produces = "application/json")
List<Comment> comments(@RequestHeader(value="x-auth-token") String token) {
  User.assertAuth(secret, token);
  return Comment.fetch_all();
}

🏷️ Tags

corscross-originweb-securityconfiguration
LOW CWE-532 HIGH ✓ VERIFIED TRUE POSITIVE
ID: fb2710063a16

Information Disclosure - System Information Leakage via Console Output

Insertion of Sensitive Information into Log File

📍 LinkLister.java : Line 25
Full Path: src/main/java/com/scalesec/vulnado/LinkLister.java
Sink Method: com.scalesec.vulnado.LinkLister.getLinksV2

⚠️ Vulnerable Code

System.out.println(host);

📄 Surrounding Code Context (±5 lines)

21:   public static List<String> getLinksV2(String url) throws BadRequest {
22:     try {
23:       URL aUrl= new URL(url);
24:       String host = aUrl.getHost();
25:       System.out.println(host);
26:       if (host.startsWith("172.") || host.startsWith("192.168") || host.startsWith("10.")){
27:         throw new BadRequest("Use of Private IP");
28:       } else {
29:         return getLinks(url);
30:       }
31:     } catch(Exception e) {

💡 Explanation

User-controlled input (hostnames from URLs) is being logged to system output without sanitization. This can lead to: 1) Log injection attacks where malicious hostnames containing control characters corrupt log files, 2) Information disclosure if logs are accessible to unauthorized users, 3) Log forging where attackers inject fake log entries, 4) Potential system information leakage about internal network topology through hostname resolution attempts.

🔗 Tainted Variables

urlhost

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] src/main/java/com/scalesec/vulnado/LinksController.java:17 (HTTP parameter)
    List<String> linksV2(@RequestParam String url) throws BadRequest{url
  2. Step 2 [PROPAGATION] src/main/java/com/scalesec/vulnado/LinkLister.java:23
    URL aUrl= new URL(url);url
  3. Step 3 [PROPAGATION] src/main/java/com/scalesec/vulnado/LinkLister.java:24
    String host = aUrl.getHost();host
  4. Step 4 [SINK] src/main/java/com/scalesec/vulnado/LinkLister.java:25
    System.out.println(host);host

✅ Recommended Fix

1) Remove debug System.out.println statements from production code, 2) Use proper logging framework (SLF4J, Logback) with appropriate log levels, 3) Sanitize user input before logging, 4) Implement log rotation and access controls, 5) Consider logging only necessary information for debugging purposes in development environments only.

// Remove the System.out.println(host); line entirely
// Or replace with proper logging:
private static final Logger logger = LoggerFactory.getLogger(LinkLister.class);

public static List<String> getLinksV2(String url) throws BadRequest {
    try {
        URL aUrl = new URL(url);
        String host = aUrl.getHost();
        
        // Use proper logging with sanitization if needed for debugging
        if (logger.isDebugEnabled()) {
            logger.debug("Processing request for host: {}", sanitizeForLogging(host));
        }
        
        if (host.startsWith("172.") || host.startsWith("192.168") || host.startsWith("10.")) {
            throw new BadRequest("Use of Private IP");
        } else {
            return getLinks(url);
        }
    } catch (Exception e) {
        throw new BadRequest(e.getMessage());
    }
}

private static String sanitizeForLogging(String input) {
    return input.replaceAll("[\r\n\t]", "_");
}

🏷️ Tags

information-disclosureloggingdebug-codelog-injection