From bfe68fb83e9271d2d1fef3549ce6ad06c315321a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 29 Jul 2025 20:32:37 +0000 Subject: [PATCH 01/17] Initial plan From 7a4d27435f7c95640ad201c6f4a0d66bcdacb679 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 29 Jul 2025 20:51:56 +0000 Subject: [PATCH 02/17] Implement Challenge 57: Database Connection String Exposure through Error Messages Co-authored-by: commjoen <1457214+commjoen@users.noreply.github.com> --- README.md | 6 +- .../challenges/docker/Challenge57.java | 57 +++++++++++++++++++ .../docker/Challenge57Controller.java | 25 ++++++++ .../resources/explanations/challenge57.adoc | 33 +++++++++++ .../explanations/challenge57_hint.adoc | 1 + .../explanations/challenge57_reason.adoc | 43 ++++++++++++++ .../wrong-secrets-configuration.yaml | 13 +++++ .../docker/Challenge57ControllerTest.java | 31 ++++++++++ .../challenges/docker/Challenge57Test.java | 47 +++++++++++++++ 9 files changed, 253 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57.java create mode 100644 src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Controller.java create mode 100644 src/main/resources/explanations/challenge57.adoc create mode 100644 src/main/resources/explanations/challenge57_hint.adoc create mode 100644 src/main/resources/explanations/challenge57_reason.adoc create mode 100644 src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57ControllerTest.java create mode 100644 src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Test.java diff --git a/README.md b/README.md index b2b2067db..ae01ddba9 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Welcome to the OWASP WrongSecrets game! The game is packed with real life examples of how to _not_ store secrets in your software. Each of these examples is captured in a challenge, which you need to solve using various tools and techniques. Solving these challenges will help you recognize common mistakes & can help you to reflect on your own secrets management strategy. -Can you solve all the 56 challenges? +Can you solve all the 57 challenges? Try some of them on [our Heroku demo environment](https://wrongsecrets.herokuapp.com/). @@ -130,7 +130,7 @@ Not sure which setup is right for you? Here's a quick guide: | Try it quickly online | [Container running on Heroku](https://www.wrongsecrets.com/) | Basic challenges (1-4, 8, 12-32, 34-43, 49-52, 54-55) | | Run locally with Docker | [Basic Docker](#basic-docker-exercises) | Same as above, but on your machine | | Learn Kubernetes secrets | [K8s/Minikube Setup](#basic-k8s-exercise) | Kubernetes challenges (1-6, 8, 12-43, 48-55) | -| Practice with cloud secrets | [Cloud Challenges](#cloud-challenges) | All challenges (1-55) | +| Practice with cloud secrets | [Cloud Challenges](#cloud-challenges) | All challenges (1-57) | | Run a workshop/CTF | [CTF Setup](#ctf) | Customizable challenge sets | | Contribute to the project | [Development Setup](#notes-on-development) | All challenges + development tools | @@ -330,7 +330,7 @@ This is because if you run the start script again it will replace the secret in ## Cloud Challenges -_Can be used for challenges 1-55_ +_Can be used for challenges 1-57_ **READ THIS**: Given that the exercises below contain IAM privilege escalation exercises, never run this on an account which is related to your production environment or can influence your account-over-arching diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57.java new file mode 100644 index 000000000..1064cf1f1 --- /dev/null +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57.java @@ -0,0 +1,57 @@ +package org.owasp.wrongsecrets.challenges.docker; + +import com.google.common.base.Strings; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import lombok.extern.slf4j.Slf4j; +import org.owasp.wrongsecrets.challenges.Challenge; +import org.owasp.wrongsecrets.challenges.Spoiler; +import org.springframework.stereotype.Component; + +/** Challenge demonstrating database connection string exposure through error messages. */ +@Slf4j +@Component +public class Challenge57 implements Challenge { + + // Simulated database connection string with embedded credentials + private static final String DB_CONNECTION_STRING = + "jdbc:postgresql://db.example.com:5432/userdb?user=dbadmin&password=SuperSecretDB2024!&ssl=true"; + + private static final String EXPECTED_SECRET = "SuperSecretDB2024!"; + + @Override + public Spoiler spoiler() { + return new Spoiler(EXPECTED_SECRET); + } + + @Override + public boolean answerCorrect(String answer) { + return !Strings.isNullOrEmpty(answer) && EXPECTED_SECRET.equals(answer.trim()); + } + + /** + * This method simulates what happens when an application tries to connect to a database + * but fails, exposing the full connection string (including credentials) in error messages. + * This is a common real-world mistake where developers include sensitive information + * in connection strings and don't properly handle/sanitize database connection errors. + */ + public String simulateDatabaseConnectionError() { + try { + // This will fail since we don't have a real database, but it demonstrates + // how connection errors can expose credentials + Connection conn = DriverManager.getConnection(DB_CONNECTION_STRING); + conn.close(); + return "Connection successful"; + } catch (SQLException e) { + // Poor error handling - exposing the full connection string in the error message + String errorMessage = "Database connection failed with connection string: " + DB_CONNECTION_STRING + + "\nError: " + e.getMessage(); + + // Log the error (another way credentials get exposed) + log.error("Failed to connect to database: {}", errorMessage); + + return errorMessage; + } + } +} \ No newline at end of file diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Controller.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Controller.java new file mode 100644 index 000000000..072aa2e66 --- /dev/null +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Controller.java @@ -0,0 +1,25 @@ +package org.owasp.wrongsecrets.challenges.docker; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** REST controller for Challenge 57 to trigger database connection error. */ +@Slf4j +@RestController +@RequiredArgsConstructor +public class Challenge57Controller { + + private final Challenge57 challenge; + + /** + * Endpoint to trigger a database connection error that exposes connection string with credentials. + * This simulates what happens when applications try to connect to unavailable databases. + */ + @GetMapping("/error-demo/database-connection") + public String triggerDatabaseError() { + log.info("Attempting database connection for Challenge 57..."); + return challenge.simulateDatabaseConnectionError(); + } +} \ No newline at end of file diff --git a/src/main/resources/explanations/challenge57.adoc b/src/main/resources/explanations/challenge57.adoc new file mode 100644 index 000000000..2957c96c3 --- /dev/null +++ b/src/main/resources/explanations/challenge57.adoc @@ -0,0 +1,33 @@ +=== Database Connection String Exposure in Error Messages + +One of the most common and dangerous ways secrets leak in real-world applications is through database connection strings that contain embedded credentials. When applications fail to connect to databases, they often expose the full connection string (including usernames and passwords) in error messages, logs, or even user-facing interfaces. + +This challenge demonstrates a scenario where a developer: + +1. **Uses embedded credentials in connection strings** instead of external secret management +2. **Has poor error handling** that exposes the full connection string when database connections fail +3. **Logs sensitive information** without sanitizing credentials first +4. **Displays technical details** that could reach monitoring systems, error tracking tools, or even end users + +**Common places where these exposed connection strings appear:** + +- Application startup logs when database is unavailable +- Exception stack traces in monitoring tools like Sentry, Rollbar, or CloudWatch +- Error messages displayed to users during maintenance windows +- CI/CD pipeline logs when deployment health checks fail +- Docker container logs during orchestration failures + +**Real-world examples:** + +- Applications that fail health checks during Kubernetes deployments +- Microservices that can't reach their database during startup +- Database migration scripts that fail with exposed connection details +- Development/testing environments where error verbosity is set too high + +**How to trigger the error:** + +Visit the `/error-demo/database-connection` endpoint to simulate a database connection failure. This endpoint attempts to connect to a database using a connection string with embedded credentials, and when it fails, it exposes the credentials in both the HTTP response and application logs. + +Can you find the database password that gets exposed when the application tries to connect to the database? + +**Hint:** Look for database connection error messages that reveal more than they should. \ No newline at end of file diff --git a/src/main/resources/explanations/challenge57_hint.adoc b/src/main/resources/explanations/challenge57_hint.adoc new file mode 100644 index 000000000..64b37a7e0 --- /dev/null +++ b/src/main/resources/explanations/challenge57_hint.adoc @@ -0,0 +1 @@ +Try visiting the `/error-demo/database-connection` endpoint to trigger a database connection error. Look at both the HTTP response and the application logs - database connection failures often expose connection strings with embedded credentials in error messages. \ No newline at end of file diff --git a/src/main/resources/explanations/challenge57_reason.adoc b/src/main/resources/explanations/challenge57_reason.adoc new file mode 100644 index 000000000..e15fc2a31 --- /dev/null +++ b/src/main/resources/explanations/challenge57_reason.adoc @@ -0,0 +1,43 @@ +=== What went wrong? + +The application demonstrates several critical security mistakes commonly found in real-world applications: + +**1. Embedded Credentials in Connection Strings** +The database connection string contains the username and password directly embedded: +``` +jdbc:postgresql://db.example.com:5432/userdb?user=dbadmin&password=SuperSecretDB2024!&ssl=true +``` + +**2. Poor Error Handling** +When the database connection fails, the application exposes the entire connection string in the error message, revealing the embedded credentials to anyone who can see the error. + +**3. Sensitive Information in Logs** +The error gets logged with the full connection string, meaning the credentials are now stored in log files that may be: +- Accessible to multiple team members +- Shipped to centralized logging systems +- Stored long-term in log archives +- Exposed through log monitoring tools + +**4. No Secret Sanitization** +The application doesn't sanitize sensitive information before logging or displaying errors. + +**How to fix this:** + +1. **Use external secret management:** Store database credentials in environment variables, secret management systems (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault), or configuration files not checked into version control. + +2. **Separate connection parameters:** Keep the connection URL separate from credentials: + ``` + DB_HOST=db.example.com + DB_PORT=5432 + DB_NAME=userdb + DB_USER=dbadmin + DB_PASS=SuperSecretDB2024! + ``` + +3. **Implement proper error handling:** Sanitize error messages before logging or displaying them. Never include connection strings or other sensitive data in error messages. + +4. **Use connection pooling with secure configuration:** Modern frameworks like Spring Boot support secure configuration of connection pools without exposing credentials. + +5. **Monitor and audit:** Regularly review logs and error messages to ensure sensitive information isn't being inadvertently exposed. + +This type of credential exposure is extremely common in production applications and represents one of the most frequent ways database credentials are accidentally leaked. \ No newline at end of file diff --git a/src/main/resources/wrong-secrets-configuration.yaml b/src/main/resources/wrong-secrets-configuration.yaml index d83e16f8f..0bc8a3097 100644 --- a/src/main/resources/wrong-secrets-configuration.yaml +++ b/src/main/resources/wrong-secrets-configuration.yaml @@ -879,3 +879,16 @@ configurations: category: *ai ctf: enabled: true + + - name: Challenge 57 + short-name: "challenge-57" + sources: + - class-name: "org.owasp.wrongsecrets.challenges.docker.Challenge57" + explanation: "explanations/challenge57.adoc" + hint: "explanations/challenge57_hint.adoc" + reason: "explanations/challenge57_reason.adoc" + environments: *all_envs + difficulty: *normal + category: *logging + ctf: + enabled: true diff --git a/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57ControllerTest.java b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57ControllerTest.java new file mode 100644 index 000000000..f91d4bff1 --- /dev/null +++ b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57ControllerTest.java @@ -0,0 +1,31 @@ +package org.owasp.wrongsecrets.challenges.docker; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class Challenge57ControllerTest { + + @Mock private Challenge57 challenge; + + @InjectMocks private Challenge57Controller controller; + + @Test + void triggerDatabaseErrorShouldReturnErrorMessage() { + // Given + String expectedError = "Database connection failed with connection string: ..."; + org.mockito.Mockito.when(challenge.simulateDatabaseConnectionError()).thenReturn(expectedError); + + // When + String result = controller.triggerDatabaseError(); + + // Then + assertThat(result).isEqualTo(expectedError); + org.mockito.Mockito.verify(challenge).simulateDatabaseConnectionError(); + } +} \ No newline at end of file diff --git a/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Test.java b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Test.java new file mode 100644 index 000000000..8c80ad0b2 --- /dev/null +++ b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Test.java @@ -0,0 +1,47 @@ +package org.owasp.wrongsecrets.challenges.docker; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.owasp.wrongsecrets.challenges.Spoiler; + +class Challenge57Test { + + @Test + void spoilerShouldReturnCorrectAnswer() { + var challenge = new Challenge57(); + assertThat(challenge.spoiler()).isEqualTo(new Spoiler("SuperSecretDB2024!")); + } + + @Test + void answerCorrectShouldReturnTrueForCorrectAnswer() { + var challenge = new Challenge57(); + assertThat(challenge.answerCorrect("SuperSecretDB2024!")).isTrue(); + } + + @Test + void answerCorrectShouldReturnFalseForIncorrectAnswer() { + var challenge = new Challenge57(); + assertThat(challenge.answerCorrect("wronganswer")).isFalse(); + assertThat(challenge.answerCorrect("")).isFalse(); + assertThat(challenge.answerCorrect(null)).isFalse(); + } + + @Test + void answerCorrectShouldTrimWhitespace() { + var challenge = new Challenge57(); + assertThat(challenge.answerCorrect(" SuperSecretDB2024! ")).isTrue(); + } + + @Test + void simulateDatabaseConnectionErrorShouldExposeConnectionString() { + var challenge = new Challenge57(); + String errorMessage = challenge.simulateDatabaseConnectionError(); + + // Verify that the error message contains the exposed connection string + assertThat(errorMessage).contains("jdbc:postgresql://db.example.com:5432/userdb"); + assertThat(errorMessage).contains("user=dbadmin"); + assertThat(errorMessage).contains("password=SuperSecretDB2024!"); + assertThat(errorMessage).contains("Database connection failed"); + } +} \ No newline at end of file From 372f123c07387180e7e5c64ede8901d91c1bbd30 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 29 Jul 2025 21:22:03 +0000 Subject: [PATCH 03/17] Fix formatting issues in Challenge 57 files Co-authored-by: commjoen <1457214+commjoen@users.noreply.github.com> --- .../challenges/docker/Challenge57.java | 27 ++++++++++--------- .../docker/Challenge57Controller.java | 7 ++--- .../resources/explanations/challenge57.adoc | 2 +- .../explanations/challenge57_hint.adoc | 2 +- .../explanations/challenge57_reason.adoc | 2 +- .../docker/Challenge57ControllerTest.java | 2 +- .../challenges/docker/Challenge57Test.java | 4 +-- 7 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57.java index 1064cf1f1..a585f688b 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57.java @@ -15,9 +15,9 @@ public class Challenge57 implements Challenge { // Simulated database connection string with embedded credentials - private static final String DB_CONNECTION_STRING = + private static final String DB_CONNECTION_STRING = "jdbc:postgresql://db.example.com:5432/userdb?user=dbadmin&password=SuperSecretDB2024!&ssl=true"; - + private static final String EXPECTED_SECRET = "SuperSecretDB2024!"; @Override @@ -31,27 +31,30 @@ public boolean answerCorrect(String answer) { } /** - * This method simulates what happens when an application tries to connect to a database - * but fails, exposing the full connection string (including credentials) in error messages. - * This is a common real-world mistake where developers include sensitive information - * in connection strings and don't properly handle/sanitize database connection errors. + * This method simulates what happens when an application tries to connect to a database but + * fails, exposing the full connection string (including credentials) in error messages. This is a + * common real-world mistake where developers include sensitive information in connection strings + * and don't properly handle/sanitize database connection errors. */ public String simulateDatabaseConnectionError() { try { - // This will fail since we don't have a real database, but it demonstrates + // This will fail since we don't have a real database, but it demonstrates // how connection errors can expose credentials Connection conn = DriverManager.getConnection(DB_CONNECTION_STRING); conn.close(); return "Connection successful"; } catch (SQLException e) { // Poor error handling - exposing the full connection string in the error message - String errorMessage = "Database connection failed with connection string: " + DB_CONNECTION_STRING - + "\nError: " + e.getMessage(); - + String errorMessage = + "Database connection failed with connection string: " + + DB_CONNECTION_STRING + + "\nError: " + + e.getMessage(); + // Log the error (another way credentials get exposed) log.error("Failed to connect to database: {}", errorMessage); - + return errorMessage; } } -} \ No newline at end of file +} diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Controller.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Controller.java index 072aa2e66..7926bac4c 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Controller.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Controller.java @@ -14,12 +14,13 @@ public class Challenge57Controller { private final Challenge57 challenge; /** - * Endpoint to trigger a database connection error that exposes connection string with credentials. - * This simulates what happens when applications try to connect to unavailable databases. + * Endpoint to trigger a database connection error that exposes connection string with + * credentials. This simulates what happens when applications try to connect to unavailable + * databases. */ @GetMapping("/error-demo/database-connection") public String triggerDatabaseError() { log.info("Attempting database connection for Challenge 57..."); return challenge.simulateDatabaseConnectionError(); } -} \ No newline at end of file +} diff --git a/src/main/resources/explanations/challenge57.adoc b/src/main/resources/explanations/challenge57.adoc index 2957c96c3..378fd5a45 100644 --- a/src/main/resources/explanations/challenge57.adoc +++ b/src/main/resources/explanations/challenge57.adoc @@ -30,4 +30,4 @@ Visit the `/error-demo/database-connection` endpoint to simulate a database conn Can you find the database password that gets exposed when the application tries to connect to the database? -**Hint:** Look for database connection error messages that reveal more than they should. \ No newline at end of file +**Hint:** Look for database connection error messages that reveal more than they should. diff --git a/src/main/resources/explanations/challenge57_hint.adoc b/src/main/resources/explanations/challenge57_hint.adoc index 64b37a7e0..fef53235a 100644 --- a/src/main/resources/explanations/challenge57_hint.adoc +++ b/src/main/resources/explanations/challenge57_hint.adoc @@ -1 +1 @@ -Try visiting the `/error-demo/database-connection` endpoint to trigger a database connection error. Look at both the HTTP response and the application logs - database connection failures often expose connection strings with embedded credentials in error messages. \ No newline at end of file +Try visiting the `/error-demo/database-connection` endpoint to trigger a database connection error. Look at both the HTTP response and the application logs - database connection failures often expose connection strings with embedded credentials in error messages. diff --git a/src/main/resources/explanations/challenge57_reason.adoc b/src/main/resources/explanations/challenge57_reason.adoc index e15fc2a31..6d6aa51df 100644 --- a/src/main/resources/explanations/challenge57_reason.adoc +++ b/src/main/resources/explanations/challenge57_reason.adoc @@ -40,4 +40,4 @@ The application doesn't sanitize sensitive information before logging or display 5. **Monitor and audit:** Regularly review logs and error messages to ensure sensitive information isn't being inadvertently exposed. -This type of credential exposure is extremely common in production applications and represents one of the most frequent ways database credentials are accidentally leaked. \ No newline at end of file +This type of credential exposure is extremely common in production applications and represents one of the most frequent ways database credentials are accidentally leaked. diff --git a/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57ControllerTest.java b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57ControllerTest.java index f91d4bff1..9492f4760 100644 --- a/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57ControllerTest.java +++ b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57ControllerTest.java @@ -28,4 +28,4 @@ void triggerDatabaseErrorShouldReturnErrorMessage() { assertThat(result).isEqualTo(expectedError); org.mockito.Mockito.verify(challenge).simulateDatabaseConnectionError(); } -} \ No newline at end of file +} diff --git a/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Test.java b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Test.java index 8c80ad0b2..0ea33f215 100644 --- a/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Test.java +++ b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Test.java @@ -37,11 +37,11 @@ void answerCorrectShouldTrimWhitespace() { void simulateDatabaseConnectionErrorShouldExposeConnectionString() { var challenge = new Challenge57(); String errorMessage = challenge.simulateDatabaseConnectionError(); - + // Verify that the error message contains the exposed connection string assertThat(errorMessage).contains("jdbc:postgresql://db.example.com:5432/userdb"); assertThat(errorMessage).contains("user=dbadmin"); assertThat(errorMessage).contains("password=SuperSecretDB2024!"); assertThat(errorMessage).contains("Database connection failed"); } -} \ No newline at end of file +} From cc5f015d655a457906566d3a3053f7b6fbe3d293 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 2 Aug 2025 23:24:13 +0000 Subject: [PATCH 04/17] Fix GitHub Pages workflow artifact naming conflict - Give unique names to artifacts in different jobs (github-pages-preview vs github-pages-cleanup) - Add explicit artifact_name parameter to deploy-pages actions - Resolves "Multiple artifacts named 'github-pages'" error in workflow runs Co-authored-by: commjoen <1457214+commjoen@users.noreply.github.com> --- .github/workflows/github-pages-preview.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/github-pages-preview.yml b/.github/workflows/github-pages-preview.yml index 2ad8e2bb9..66a66a555 100644 --- a/.github/workflows/github-pages-preview.yml +++ b/.github/workflows/github-pages-preview.yml @@ -211,10 +211,13 @@ jobs: uses: actions/upload-pages-artifact@v3 with: path: ./static-site + name: github-pages-preview - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 + with: + artifact_name: github-pages-preview - name: Comment PR with preview link uses: actions/github-script@v7 @@ -318,10 +321,13 @@ jobs: uses: actions/upload-pages-artifact@v3 with: path: ./static-site + name: github-pages-cleanup - name: Deploy cleaned pages id: deployment uses: actions/deploy-pages@v4 + with: + artifact_name: github-pages-cleanup - name: Comment PR cleanup completion uses: actions/github-script@v7 From 161cec1c873e432e25bea631b3858b17f35bb657 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 2 Aug 2025 23:55:45 +0000 Subject: [PATCH 05/17] Implement Challenge 58 and update Challenge 57 with complete GitHub Pages preview support Co-authored-by: commjoen <1457214+commjoen@users.noreply.github.com> --- .../scripts/generate_thymeleaf_previews.py | 342 ++++- .github/workflows/github-pages-preview.yml | 8 + README.md | 2 +- .../challenges/docker/Challenge57.java | 131 +- .../docker/Challenge57Controller.java | 62 +- .../challenges/docker/Challenge58.java | 60 + .../docker/Challenge58Controller.java | 26 + .../resources/explanations/challenge57.adoc | 48 +- .../explanations/challenge57_hint.adoc | 36 +- .../explanations/challenge57_reason.adoc | 66 +- .../resources/explanations/challenge58.adoc | 33 + .../explanations/challenge58_hint.adoc | 25 + .../explanations/challenge58_reason.adoc | 40 + .../wrong-secrets-configuration.yaml | 13 + .../docker/Challenge57ControllerTest.java | 24 +- .../challenges/docker/Challenge57Test.java | 23 +- .../docker/Challenge58ControllerTest.java | 31 + .../challenges/docker/Challenge58Test.java | 47 + static-site/pr-123/pages/about.html | 436 +++++++ static-site/pr-123/pages/challenge-57.html | 124 ++ static-site/pr-123/pages/challenge-58.html | 131 ++ .../pr-123/pages/challenge-example.html | 128 ++ static-site/pr-123/pages/stats.html | 73 ++ static-site/pr-123/pages/welcome.html | 1144 +++++++++++++++++ 24 files changed, 2918 insertions(+), 135 deletions(-) create mode 100644 src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge58.java create mode 100644 src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge58Controller.java create mode 100644 src/main/resources/explanations/challenge58.adoc create mode 100644 src/main/resources/explanations/challenge58_hint.adoc create mode 100644 src/main/resources/explanations/challenge58_reason.adoc create mode 100644 src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge58ControllerTest.java create mode 100644 src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge58Test.java create mode 100644 static-site/pr-123/pages/about.html create mode 100644 static-site/pr-123/pages/challenge-57.html create mode 100644 static-site/pr-123/pages/challenge-58.html create mode 100644 static-site/pr-123/pages/challenge-example.html create mode 100644 static-site/pr-123/pages/stats.html create mode 100644 static-site/pr-123/pages/welcome.html diff --git a/.github/scripts/generate_thymeleaf_previews.py b/.github/scripts/generate_thymeleaf_previews.py index d761f1ba7..4e3a301a2 100755 --- a/.github/scripts/generate_thymeleaf_previews.py +++ b/.github/scripts/generate_thymeleaf_previews.py @@ -55,32 +55,72 @@ def generate_mock_challenges(self): "Find the secret in the container", "Retrieve cloud instance metadata", "Use AWS Parameter Store", + "Find the secret in SSM", + "Find the Docker secret", + "GitHub Actions secret", + "Find the K8s secret", + "Find the hardcoded secret", + "Front-end secret exposure", + "Bash history secret", + "Find the secret in logs", + "Find the encrypted secret", + "Binary analysis secret", + "Go binary secret", + "Rust binary secret", + "Front-end secret part 2", + "Web3 secret exposure", + "Smart contract secret", + "Binary secret 2", + "Terraform secret", + "GitHub issue secret", + "Log analysis secret", + "Web page secret", + "AI prompt injection", + "Container debugging", + "Password shucking", + "Random key generation", + "Vulnerability reporting", + "Binary without strings", + "Security test access", + "Git notes secret", + "Insecure encryption key", + "Encryption key storage", + "Password shucking 2", + "Audit event secret", + "Vault template injection", + "Multi-environment secret", + "Vault subkey challenge", + "HashiCorp Vault injection", + "Binary secret 3", + "Binary secret 4", + "AES MD5 cracking", + "Docker secret 2", + "Binary secret 5", + "Docker secret 3", + "Container debugging 2", + "Docker secret 4", + "SSH bastion secret", + "AI secret exposure", + "LLM API key exposure in JavaScript", # Challenge 57 + "Database connection string exposure", # Challenge 58 ] difficulties = ["⭐", "⭐⭐", "⭐⭐⭐", "⭐⭐⭐⭐", "⭐⭐⭐⭐⭐"] techs = [ - "DEVOPS", - "GIT", - "FRONTEND", - "DEVOPS", - "AWS", - "AZURE", - "DOCKER", - "DOCKER", - "AWS", - "AWS", + "DEVOPS", "GIT", "FRONTEND", "DEVOPS", "AWS", "AZURE", "DOCKER", "DOCKER", "AWS", "AWS", + "AWS", "DOCKER", "CI/CD", "K8S", "DEVOPS", "FRONTEND", "DEVOPS", "LOGGING", "CRYPTO", "BINARY", + "BINARY", "BINARY", "FRONTEND", "WEB3", "WEB3", "BINARY", "TERRAFORM", "GIT", "LOGGING", "FRONTEND", + "AI", "DOCKER", "PASSWORD", "CRYPTO", "DOCS", "BINARY", "CI/CD", "GIT", "CRYPTO", "CRYPTO", + "PASSWORD", "LOGGING", "VAULT", "VAULT", "VAULT", "VAULT", "BINARY", "BINARY", "CRYPTO", "DOCKER", + "BINARY", "DOCKER", "DOCKER", "DOCKER", "DOCKER", "AI", "AI", "LOGGING" ] environments = [ - "Docker", - "Docker", - "Docker", - "Docker", - "AWS", - "Azure", - "Docker", - "Docker", - "AWS", - "AWS", + "Docker", "Docker", "Docker", "Docker", "AWS", "Azure", "Docker", "Docker", "AWS", "AWS", + "AWS", "Docker", "Docker", "K8s", "Docker", "Docker", "Docker", "Docker", "Docker", "Docker", + "Docker", "Docker", "Docker", "Docker", "Docker", "Docker", "AWS", "Docker", "Docker", "Docker", + "Docker", "K8s", "Docker", "Docker", "Docker", "Docker", "Docker", "Docker", "Docker", "Docker", + "Docker", "Docker", "K8s with Vault", "K8s with Vault", "K8s with Vault", "K8s with Vault", "Docker", "Docker", "Docker", "Docker", + "Docker", "Docker", "K8s", "Docker", "Docker", "Docker", "Docker", "Docker" ] for i, name in enumerate(challenge_names): @@ -416,6 +456,266 @@ def generate_stats_page(self): return content + def generate_challenge_57_preview(self): + """Generate a specific preview page for Challenge 57 (LLM).""" + return """ + + + + + Challenge 57: LLM API Key Exposure - Preview + + + + + +
+
+

📋 Challenge 57 Preview

+

This is a static preview of Challenge 57: LLM API Key Exposure in Client-Side JavaScript

+
+ +
+
+

🤖 Challenge 57: LLM API Key Exposure

+
+ AI + ⭐⭐ +
+
+ +
+
+

📋 Challenge Description

+

This challenge demonstrates a critical security vulnerability in modern AI-powered web applications: LLM API keys exposed in client-side JavaScript code.

+ +

As developers rush to integrate AI capabilities, many make the critical mistake of putting sensitive API credentials directly in browser-accessible code.

+ +
+ ⚠️ Vulnerability: API keys exposed in client-side JavaScript can lead to massive financial losses, service disruption, and data harvesting. +
+ +
🎯 Your Mission
+

Find the exposed LLM API key in the client-side JavaScript code. Look for:

+
    +
  • API keys starting with "sk-"
  • +
  • JavaScript variables storing credentials
  • +
  • Console.log statements with sensitive data
  • +
  • Authorization headers in network requests
  • +
+
+ +
+

💻 Code Preview

+
+// AI Chat Application - Client-side JavaScript +class LLMChatApp { + constructor() { + // WARNING: This is a security anti-pattern! + // API keys should NEVER be exposed in client-side code + this.apiKey = 'sk-llm-api-key-abc123def456ghi789jkl012mno345pqr678stu901vwx234yzA'; + this.apiEndpoint = 'https://api.example-llm.com/v1/chat/completions'; + this.initializeChat(); + } + + async sendMessage() { + try { + const response = await fetch(this.apiEndpoint, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${this.apiKey}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + model: 'gpt-3.5-turbo', + messages: [{role: 'user', content: message}] + }) + }); + } catch (error) { + console.log('Failed request used API key:', this.apiKey); + } + } +} + +// Debug: Log the API key for development (another anti-pattern!) +console.log('Debug: LLM API Key = sk-llm-api-key-abc123def456ghi789jkl012mno345pqr678stu901vwx234yzA'); +
+ +
+
🔍 How to Explore:
+
    +
  1. Visit /llm-demo to see the vulnerable chat application
  2. +
  3. Open Developer Tools (F12)
  4. +
  5. Check the /llm-chat.js file
  6. +
  7. Monitor console for debug messages
  8. +
  9. Examine network requests
  10. +
+
+
+
+ +
+

🏆 Learning Objectives

+
+
+ 💰 Financial Impact +

LLM API calls can be extremely expensive. Exposed keys have led to bills of tens of thousands of dollars within hours.

+
+
+ 🔒 Security Risks +

Attackers can use your API keys for data harvesting, service disruption, and generating harmful content.

+
+
+ 🛡️ Prevention +

Always use server-side proxies, environment variables, and proper access controls for AI service credentials.

+
+
+
+
+
+ +""" + + def generate_challenge_58_preview(self): + """Generate a specific preview page for Challenge 58 (Database).""" + return """ + + + + + Challenge 58: Database Connection String Exposure - Preview + + + + + +
+
+

📋 Challenge 58 Preview

+

This is a static preview of Challenge 58: Database Connection String Exposure through Error Messages

+
+ +
+
+

🗄️ Challenge 58: Database Connection String Exposure

+
+ LOGGING + ⭐⭐ +
+
+ +
+
+

📋 Challenge Description

+

This challenge demonstrates one of the most common and dangerous ways secrets leak in real-world applications: database connection strings with embedded credentials exposed through error messages.

+ +

When applications fail to connect to databases, they often expose the full connection string (including usernames and passwords) in error messages, logs, or even user-facing interfaces.

+ +
+ ⚠️ Critical Risk: Database connection string exposure can lead to direct database access and complete data breaches. +
+ +
🎯 Your Mission
+

Trigger a database connection error and find the exposed password. Look for:

+
    +
  • JDBC connection URLs with embedded credentials
  • +
  • Error messages containing connection strings
  • +
  • Log entries with sensitive information
  • +
  • Patterns like password=SECRET
  • +
+
+ +
+

💻 Vulnerable Code Example

+
+// Simulated database connection string with embedded credentials +private static final String DB_CONNECTION_STRING = + "jdbc:postgresql://db.example.com:5432/userdb?" + + "user=dbadmin&password=SuperSecretDB2024!&ssl=true"; + +public String simulateDatabaseConnectionError() { + try { + // This will fail and expose the connection string + Connection conn = DriverManager.getConnection(DB_CONNECTION_STRING); + return "Connection successful"; + } catch (SQLException e) { + // Poor error handling - exposing full connection string + String errorMessage = + "Database connection failed with connection string: " + + DB_CONNECTION_STRING + "\nError: " + e.getMessage(); + + // Credentials also get logged (another exposure vector) + log.error("Failed to connect to database: {}", errorMessage); + + return errorMessage; + } +} +
+ +
+ Example Error Output:
+ Database connection failed with connection string: jdbc:postgresql://db.example.com:5432/userdb?user=dbadmin&password=SuperSecretDB2024!&ssl=true +
+
+
+ +
+

🔍 How to Trigger the Error

+

Visit the /error-demo/database-connection endpoint to simulate a database connection failure that exposes credentials in both the HTTP response and application logs.

+
+ +
+

🏆 Learning Objectives

+
+
+ 🚨 Common Exposure Vectors +
    +
  • Application startup logs
  • +
  • Health check failures
  • +
  • CI/CD pipeline logs
  • +
  • Error tracking services
  • +
+
+
+ 💥 Real-World Impact +
    +
  • Production database compromises
  • +
  • Complete data breaches
  • +
  • Lateral movement attacks
  • +
  • Compliance violations
  • +
+
+
+ 🛡️ Prevention +
    +
  • External secret management
  • +
  • Error message sanitization
  • +
  • Separate database credentials
  • +
  • Connection pooling
  • +
+
+
+
+
+
+ +""" + def generate_challenge_page(self): """Generate an example challenge page.""" template_path = self.templates_dir / "challenge.html" @@ -552,6 +852,8 @@ def generate_all_pages(self): "about.html": self.generate_about_page(), "stats.html": self.generate_stats_page(), "challenge-example.html": self.generate_challenge_page(), + "challenge-57.html": self.generate_challenge_57_preview(), + "challenge-58.html": self.generate_challenge_58_preview(), } for filename, content in pages.items(): diff --git a/.github/workflows/github-pages-preview.yml b/.github/workflows/github-pages-preview.yml index 66a66a555..a31a8caf7 100644 --- a/.github/workflows/github-pages-preview.yml +++ b/.github/workflows/github-pages-preview.yml @@ -112,6 +112,9 @@ jobs: ℹ️ About Page + + 🤖 Challenge 57: LLM API Key Exposure +
@@ -122,6 +125,9 @@ jobs: 🧩 Challenge Example + + 🗄️ Challenge 58: Database Connection Exposure +
@@ -240,6 +246,8 @@ jobs: - ℹ️ [About Page](${previewUrl}pages/about.html) - 📊 [Stats & Config Page](${previewUrl}pages/stats.html) - 🧩 [Challenge Example](${previewUrl}pages/challenge-example.html) + - 🤖 [Challenge 57: LLM API Key Exposure](${previewUrl}pages/challenge-57.html) + - 🗄️ [Challenge 58: Database Connection Exposure](${previewUrl}pages/challenge-58.html) **For full functionality testing:** Use the [Docker preview](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) instead. diff --git a/README.md b/README.md index ae01ddba9..102de0a1e 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Welcome to the OWASP WrongSecrets game! The game is packed with real life examples of how to _not_ store secrets in your software. Each of these examples is captured in a challenge, which you need to solve using various tools and techniques. Solving these challenges will help you recognize common mistakes & can help you to reflect on your own secrets management strategy. -Can you solve all the 57 challenges? +Can you solve all the 58 challenges? Try some of them on [our Heroku demo environment](https://wrongsecrets.herokuapp.com/). diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57.java index a585f688b..24e2341e7 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57.java @@ -1,60 +1,121 @@ package org.owasp.wrongsecrets.challenges.docker; import com.google.common.base.Strings; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; import lombok.extern.slf4j.Slf4j; import org.owasp.wrongsecrets.challenges.Challenge; import org.owasp.wrongsecrets.challenges.Spoiler; import org.springframework.stereotype.Component; -/** Challenge demonstrating database connection string exposure through error messages. */ +/** Challenge demonstrating LLM API key exposure in client-side JavaScript. */ @Slf4j @Component public class Challenge57 implements Challenge { - // Simulated database connection string with embedded credentials - private static final String DB_CONNECTION_STRING = - "jdbc:postgresql://db.example.com:5432/userdb?user=dbadmin&password=SuperSecretDB2024!&ssl=true"; - - private static final String EXPECTED_SECRET = "SuperSecretDB2024!"; + // Simulated LLM API key that would be exposed in client-side JavaScript + private static final String LLM_API_KEY = "sk-llm-api-key-abc123def456ghi789jkl012mno345pqr678stu901vwx234yzA"; @Override public Spoiler spoiler() { - return new Spoiler(EXPECTED_SECRET); + return new Spoiler(LLM_API_KEY); } @Override public boolean answerCorrect(String answer) { - return !Strings.isNullOrEmpty(answer) && EXPECTED_SECRET.equals(answer.trim()); + return !Strings.isNullOrEmpty(answer) && LLM_API_KEY.equals(answer.trim()); } /** - * This method simulates what happens when an application tries to connect to a database but - * fails, exposing the full connection string (including credentials) in error messages. This is a - * common real-world mistake where developers include sensitive information in connection strings - * and don't properly handle/sanitize database connection errors. + * This method returns JavaScript code that would typically be served to the browser, + * containing an exposed LLM API key. This demonstrates how sensitive API keys can be + * accidentally exposed in client-side code. */ - public String simulateDatabaseConnectionError() { - try { - // This will fail since we don't have a real database, but it demonstrates - // how connection errors can expose credentials - Connection conn = DriverManager.getConnection(DB_CONNECTION_STRING); - conn.close(); - return "Connection successful"; - } catch (SQLException e) { - // Poor error handling - exposing the full connection string in the error message - String errorMessage = - "Database connection failed with connection string: " - + DB_CONNECTION_STRING - + "\nError: " - + e.getMessage(); - - // Log the error (another way credentials get exposed) - log.error("Failed to connect to database: {}", errorMessage); - - return errorMessage; - } + public String getLLMJavaScriptCode() { + return """ + // AI Chat Application - Client-side JavaScript + class LLMChatApp { + constructor() { + // WARNING: This is a security anti-pattern! + // API keys should NEVER be exposed in client-side code + this.apiKey = '%s'; + this.apiEndpoint = 'https://api.example-llm.com/v1/chat/completions'; + this.initializeChat(); + } + + async initializeChat() { + console.log('Initializing LLM chat with API key:', this.apiKey); + this.setupEventListeners(); + } + + setupEventListeners() { + document.getElementById('send-button').addEventListener('click', () => { + this.sendMessage(); + }); + + document.getElementById('message-input').addEventListener('keypress', (e) => { + if (e.key === 'Enter') { + this.sendMessage(); + } + }); + } + + async sendMessage() { + const messageInput = document.getElementById('message-input'); + const message = messageInput.value.trim(); + + if (!message) return; + + try { + const response = await fetch(this.apiEndpoint, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${this.apiKey}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + model: 'gpt-3.5-turbo', + messages: [ + {role: 'user', content: message} + ], + max_tokens: 150 + }) + }); + + const data = await response.json(); + this.displayResponse(data.choices[0].message.content); + + } catch (error) { + console.error('LLM API Error:', error); + console.log('Failed request used API key:', this.apiKey); + this.displayError('Failed to get response from LLM service'); + } + + messageInput.value = ''; + } + + displayResponse(text) { + const chatOutput = document.getElementById('chat-output'); + const responseDiv = document.createElement('div'); + responseDiv.className = 'llm-response'; + responseDiv.textContent = text; + chatOutput.appendChild(responseDiv); + chatOutput.scrollTop = chatOutput.scrollHeight; + } + + displayError(error) { + const chatOutput = document.getElementById('chat-output'); + const errorDiv = document.createElement('div'); + errorDiv.className = 'error-message'; + errorDiv.textContent = error; + chatOutput.appendChild(errorDiv); + } + } + + // Initialize the chat app when page loads + document.addEventListener('DOMContentLoaded', () => { + // Debug: Log the API key for development (another anti-pattern!) + console.log('Debug: LLM API Key = %s'); + new LLMChatApp(); + }); + """.formatted(LLM_API_KEY, LLM_API_KEY); } } diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Controller.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Controller.java index 7926bac4c..31a8525f2 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Controller.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Controller.java @@ -2,10 +2,11 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; -/** REST controller for Challenge 57 to trigger database connection error. */ +/** REST controller for Challenge 57 to serve LLM JavaScript with exposed API key. */ @Slf4j @RestController @RequiredArgsConstructor @@ -14,13 +15,58 @@ public class Challenge57Controller { private final Challenge57 challenge; /** - * Endpoint to trigger a database connection error that exposes connection string with - * credentials. This simulates what happens when applications try to connect to unavailable - * databases. + * Endpoint to serve JavaScript code that contains an exposed LLM API key. + * This simulates how developers accidentally expose API keys in client-side code. */ - @GetMapping("/error-demo/database-connection") - public String triggerDatabaseError() { - log.info("Attempting database connection for Challenge 57..."); - return challenge.simulateDatabaseConnectionError(); + @GetMapping(value = "/llm-chat.js", produces = MediaType.APPLICATION_JAVASCRIPT_VALUE) + public String getLLMJavaScript() { + log.info("Serving LLM JavaScript for Challenge 57..."); + return challenge.getLLMJavaScriptCode(); + } + + /** + * Endpoint to serve a simple HTML page that loads the vulnerable JavaScript. + */ + @GetMapping(value = "/llm-demo", produces = MediaType.TEXT_HTML_VALUE) + public String getLLMDemoPage() { + return """ + + + + + + LLM Chat Demo - Challenge 57 + + + +

🤖 LLM Chat Demo

+
+ ⚠️ Security Notice: This demo application contains a common security vulnerability. + Can you find the exposed API key? +
+ +
+
+ + +
+ +
+

Hint: Check the browser's developer tools (F12) and look at the Network tab or Console.

+

You can also view the source code of the JavaScript file directly.

+
+ + + + + """; } } diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge58.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge58.java new file mode 100644 index 000000000..7fd538f16 --- /dev/null +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge58.java @@ -0,0 +1,60 @@ +package org.owasp.wrongsecrets.challenges.docker; + +import com.google.common.base.Strings; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import lombok.extern.slf4j.Slf4j; +import org.owasp.wrongsecrets.challenges.Challenge; +import org.owasp.wrongsecrets.challenges.Spoiler; +import org.springframework.stereotype.Component; + +/** Challenge demonstrating database connection string exposure through error messages. */ +@Slf4j +@Component +public class Challenge58 implements Challenge { + + // Simulated database connection string with embedded credentials + private static final String DB_CONNECTION_STRING = + "jdbc:postgresql://db.example.com:5432/userdb?user=dbadmin&password=SuperSecretDB2024!&ssl=true"; + + private static final String EXPECTED_SECRET = "SuperSecretDB2024!"; + + @Override + public Spoiler spoiler() { + return new Spoiler(EXPECTED_SECRET); + } + + @Override + public boolean answerCorrect(String answer) { + return !Strings.isNullOrEmpty(answer) && EXPECTED_SECRET.equals(answer.trim()); + } + + /** + * This method simulates what happens when an application tries to connect to a database but + * fails, exposing the full connection string (including credentials) in error messages. This is a + * common real-world mistake where developers include sensitive information in connection strings + * and don't properly handle/sanitize database connection errors. + */ + public String simulateDatabaseConnectionError() { + try { + // This will fail since we don't have a real database, but it demonstrates + // how connection errors can expose credentials + Connection conn = DriverManager.getConnection(DB_CONNECTION_STRING); + conn.close(); + return "Connection successful"; + } catch (SQLException e) { + // Poor error handling - exposing the full connection string in the error message + String errorMessage = + "Database connection failed with connection string: " + + DB_CONNECTION_STRING + + "\nError: " + + e.getMessage(); + + // Log the error (another way credentials get exposed) + log.error("Failed to connect to database: {}", errorMessage); + + return errorMessage; + } + } +} \ No newline at end of file diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge58Controller.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge58Controller.java new file mode 100644 index 000000000..609db6eb8 --- /dev/null +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge58Controller.java @@ -0,0 +1,26 @@ +package org.owasp.wrongsecrets.challenges.docker; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** REST controller for Challenge 58 to trigger database connection error. */ +@Slf4j +@RestController +@RequiredArgsConstructor +public class Challenge58Controller { + + private final Challenge58 challenge; + + /** + * Endpoint to trigger a database connection error that exposes connection string with + * credentials. This simulates what happens when applications try to connect to unavailable + * databases. + */ + @GetMapping("/error-demo/database-connection") + public String triggerDatabaseError() { + log.info("Attempting database connection for Challenge 58..."); + return challenge.simulateDatabaseConnectionError(); + } +} \ No newline at end of file diff --git a/src/main/resources/explanations/challenge57.adoc b/src/main/resources/explanations/challenge57.adoc index 378fd5a45..668ab1acb 100644 --- a/src/main/resources/explanations/challenge57.adoc +++ b/src/main/resources/explanations/challenge57.adoc @@ -1,33 +1,37 @@ -=== Database Connection String Exposure in Error Messages +=== LLM API Key Exposure in Client-Side JavaScript -One of the most common and dangerous ways secrets leak in real-world applications is through database connection strings that contain embedded credentials. When applications fail to connect to databases, they often expose the full connection string (including usernames and passwords) in error messages, logs, or even user-facing interfaces. +As AI and Large Language Model (LLM) integrations become increasingly popular in web applications, a new class of security vulnerabilities has emerged: **LLM API key exposure in client-side code**. This challenge demonstrates one of the most dangerous mistakes developers make when building AI-powered applications. -This challenge demonstrates a scenario where a developer: +This challenge simulates a scenario where a developer: -1. **Uses embedded credentials in connection strings** instead of external secret management -2. **Has poor error handling** that exposes the full connection string when database connections fail -3. **Logs sensitive information** without sanitizing credentials first -4. **Displays technical details** that could reach monitoring systems, error tracking tools, or even end users +1. **Embeds LLM API keys directly in client-side JavaScript** instead of using server-side proxy endpoints +2. **Exposes sensitive API credentials** to anyone who can view the source code +3. **Logs API keys in browser console** for debugging purposes +4. **Makes API keys accessible** to browser extensions, XSS attacks, and web scraping -**Common places where these exposed connection strings appear:** +**Why This Is Dangerous:** -- Application startup logs when database is unavailable -- Exception stack traces in monitoring tools like Sentry, Rollbar, or CloudWatch -- Error messages displayed to users during maintenance windows -- CI/CD pipeline logs when deployment health checks fail -- Docker container logs during orchestration failures +- **Financial Impact:** Exposed LLM API keys can be used by attackers to make expensive API calls, leading to huge bills +- **Rate Limit Abuse:** Attackers can exhaust your API quotas, causing service disruptions +- **Data Extraction:** Malicious actors can use your API keys to extract training data or perform reconnaissance +- **Brand Damage:** Unauthorized use of your API keys can be associated with malicious activities -**Real-world examples:** +**Common Real-World Scenarios:** -- Applications that fail health checks during Kubernetes deployments -- Microservices that can't reach their database during startup -- Database migration scripts that fail with exposed connection details -- Development/testing environments where error verbosity is set too high +- Chat applications that call OpenAI, Anthropic, or Google AI APIs directly from the browser +- JavaScript code that includes API keys for development convenience +- Single-page applications (SPAs) with hardcoded credentials +- Debug console logs that accidentally expose API keys +- Source maps that reveal API keys from minified code -**How to trigger the error:** +**How to Explore:** -Visit the `/error-demo/database-connection` endpoint to simulate a database connection failure. This endpoint attempts to connect to a database using a connection string with embedded credentials, and when it fails, it exposes the credentials in both the HTTP response and application logs. +1. Visit `/llm-demo` to see the vulnerable chat application +2. Open your browser's Developer Tools (F12) +3. Look for exposed API keys in the JavaScript source code +4. Check the Console tab for any logged credentials +5. Examine the Network tab to see how API keys are transmitted -Can you find the database password that gets exposed when the application tries to connect to the database? +Can you find the exposed LLM API key that would allow unauthorized access to the AI service? -**Hint:** Look for database connection error messages that reveal more than they should. +**Hint:** The secret you're looking for starts with "sk-" and is a typical OpenAI-style API key format. diff --git a/src/main/resources/explanations/challenge57_hint.adoc b/src/main/resources/explanations/challenge57_hint.adoc index fef53235a..db4b05db4 100644 --- a/src/main/resources/explanations/challenge57_hint.adoc +++ b/src/main/resources/explanations/challenge57_hint.adoc @@ -1 +1,35 @@ -Try visiting the `/error-demo/database-connection` endpoint to trigger a database connection error. Look at both the HTTP response and the application logs - database connection failures often expose connection strings with embedded credentials in error messages. +=== Hint for Challenge 57 + +This challenge focuses on a critical security flaw in modern AI-powered web applications: **LLM API keys exposed in client-side JavaScript code**. + +**Where to Look:** + +1. **Visit the demo page:** Go to `/llm-demo` to see the vulnerable chat application +2. **Open Developer Tools:** Press F12 to open your browser's developer tools +3. **Check JavaScript source:** Look at the `/llm-chat.js` file that's loaded by the page +4. **Monitor the console:** Watch for any debug messages that might expose credentials +5. **Examine network requests:** See how API keys are transmitted in HTTP headers + +**What to Look For:** + +- API keys that start with "sk-" (typical OpenAI format) +- JavaScript variables that store authentication credentials +- Console.log statements that might expose sensitive data +- Authorization headers in network requests + +**Common Exposure Patterns:** + +- `this.apiKey = "sk-..."` in JavaScript classes +- `console.log()` statements with API keys for debugging +- `Authorization: Bearer ${apiKey}` in fetch requests +- Hardcoded credentials in client-side configuration objects + +**Real-World Context:** + +This vulnerability is becoming increasingly common as developers integrate AI services like: +- OpenAI GPT models +- Anthropic Claude +- Google Gemini/Bard +- Azure OpenAI Service + +**Remember:** The goal is to find the LLM API key that's exposed in the client-side JavaScript code. diff --git a/src/main/resources/explanations/challenge57_reason.adoc b/src/main/resources/explanations/challenge57_reason.adoc index 6d6aa51df..6dceb673d 100644 --- a/src/main/resources/explanations/challenge57_reason.adoc +++ b/src/main/resources/explanations/challenge57_reason.adoc @@ -1,43 +1,47 @@ -=== What went wrong? +=== Why Challenge 57 Matters: LLM API Key Exposure in Client-Side Code -The application demonstrates several critical security mistakes commonly found in real-world applications: +**The Problem:** -**1. Embedded Credentials in Connection Strings** -The database connection string contains the username and password directly embedded: -``` -jdbc:postgresql://db.example.com:5432/userdb?user=dbadmin&password=SuperSecretDB2024!&ssl=true -``` +This challenge highlights a rapidly growing security concern in the age of AI: **LLM API keys exposed in client-side JavaScript code**. As developers rush to integrate AI capabilities into their applications, many are making a critical mistake by putting sensitive API credentials directly in browser-accessible code. -**2. Poor Error Handling** -When the database connection fails, the application exposes the entire connection string in the error message, revealing the embedded credentials to anyone who can see the error. +**Why This Happens:** -**3. Sensitive Information in Logs** -The error gets logged with the full connection string, meaning the credentials are now stored in log files that may be: -- Accessible to multiple team members -- Shipped to centralized logging systems -- Stored long-term in log archives -- Exposed through log monitoring tools +1. **Rapid Development:** Developers want to quickly prototype AI features without setting up proper backend infrastructure +2. **Convenience:** It's easier to call LLM APIs directly from the frontend than to create server-side proxy endpoints +3. **Lack of Awareness:** Many developers don't realize that client-side code is completely public and inspectable +4. **Debug Practices:** API keys get logged to console during development and forgotten in production code -**4. No Secret Sanitization** -The application doesn't sanitize sensitive information before logging or displaying errors. +**Real-World Impact:** -**How to fix this:** +- **Massive Financial Losses:** LLM API calls can be extremely expensive. Exposed keys have led to bills of tens of thousands of dollars within hours +- **Service Disruption:** Attackers can exhaust your API rate limits, making your application unusable +- **Data Harvesting:** Malicious actors can use your API keys to extract information from LLM services +- **Reputation Damage:** Your API keys could be used for generating harmful content associated with your account -1. **Use external secret management:** Store database credentials in environment variables, secret management systems (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault), or configuration files not checked into version control. +**Recent Examples:** -2. **Separate connection parameters:** Keep the connection URL separate from credentials: - ``` - DB_HOST=db.example.com - DB_PORT=5432 - DB_NAME=userdb - DB_USER=dbadmin - DB_PASS=SuperSecretDB2024! - ``` +- Startups losing $10,000+ overnight due to exposed OpenAI API keys +- GitHub repositories with hardcoded API keys being scraped by bots +- Chrome extensions harvesting API keys from web applications +- Competitor analysis tools extracting LLM prompts and responses using exposed credentials -3. **Implement proper error handling:** Sanitize error messages before logging or displaying them. Never include connection strings or other sensitive data in error messages. +**Attack Vectors:** -4. **Use connection pooling with secure configuration:** Modern frameworks like Spring Boot support secure configuration of connection pools without exposing credentials. +1. **Source Code Inspection:** Anyone can view client-side JavaScript source +2. **Browser Extension Attacks:** Malicious extensions can steal API keys from memory +3. **XSS Exploitation:** Cross-site scripting attacks can extract API keys +4. **Automated Scanning:** Bots continuously scan websites for exposed credentials +5. **Web Scraping:** Automated tools can extract API keys from thousands of sites -5. **Monitor and audit:** Regularly review logs and error messages to ensure sensitive information isn't being inadvertently exposed. +**Prevention Best Practices:** -This type of credential exposure is extremely common in production applications and represents one of the most frequent ways database credentials are accidentally leaked. +1. **Server-Side Proxy:** Always proxy LLM API calls through your backend +2. **Environment Variables:** Use server-side environment variables for API keys +3. **Rate Limiting:** Implement your own rate limiting and usage controls +4. **Token-Based Auth:** Use short-lived tokens instead of permanent API keys +5. **Content Security Policy:** Implement CSP headers to limit script execution +6. **Regular Scanning:** Use tools to scan your codebase for exposed credentials + +**The Bottom Line:** + +LLM API keys should NEVER appear in client-side code. The financial and security risks are simply too high. Always use server-side proxies to protect your AI service credentials and implement proper access controls. diff --git a/src/main/resources/explanations/challenge58.adoc b/src/main/resources/explanations/challenge58.adoc new file mode 100644 index 000000000..2957c96c3 --- /dev/null +++ b/src/main/resources/explanations/challenge58.adoc @@ -0,0 +1,33 @@ +=== Database Connection String Exposure in Error Messages + +One of the most common and dangerous ways secrets leak in real-world applications is through database connection strings that contain embedded credentials. When applications fail to connect to databases, they often expose the full connection string (including usernames and passwords) in error messages, logs, or even user-facing interfaces. + +This challenge demonstrates a scenario where a developer: + +1. **Uses embedded credentials in connection strings** instead of external secret management +2. **Has poor error handling** that exposes the full connection string when database connections fail +3. **Logs sensitive information** without sanitizing credentials first +4. **Displays technical details** that could reach monitoring systems, error tracking tools, or even end users + +**Common places where these exposed connection strings appear:** + +- Application startup logs when database is unavailable +- Exception stack traces in monitoring tools like Sentry, Rollbar, or CloudWatch +- Error messages displayed to users during maintenance windows +- CI/CD pipeline logs when deployment health checks fail +- Docker container logs during orchestration failures + +**Real-world examples:** + +- Applications that fail health checks during Kubernetes deployments +- Microservices that can't reach their database during startup +- Database migration scripts that fail with exposed connection details +- Development/testing environments where error verbosity is set too high + +**How to trigger the error:** + +Visit the `/error-demo/database-connection` endpoint to simulate a database connection failure. This endpoint attempts to connect to a database using a connection string with embedded credentials, and when it fails, it exposes the credentials in both the HTTP response and application logs. + +Can you find the database password that gets exposed when the application tries to connect to the database? + +**Hint:** Look for database connection error messages that reveal more than they should. \ No newline at end of file diff --git a/src/main/resources/explanations/challenge58_hint.adoc b/src/main/resources/explanations/challenge58_hint.adoc new file mode 100644 index 000000000..ca9a67eba --- /dev/null +++ b/src/main/resources/explanations/challenge58_hint.adoc @@ -0,0 +1,25 @@ +=== Hint for Challenge 58 + +This challenge demonstrates a very common security anti-pattern: **database connection strings with embedded credentials that leak through error messages**. + +**Where to look:** + +1. **Try the error endpoint:** Visit `/error-demo/database-connection` to trigger a database connection failure +2. **Check the response:** The application will attempt to connect to a database and fail, exposing the connection string +3. **Look at logs:** The application also logs the error with the exposed credentials + +**What to look for:** + +- JDBC connection URLs often contain sensitive information +- Look for patterns like `jdbc:postgresql://...?user=USERNAME&password=PASSWORD` +- The error message will show the full connection string when the database connection fails + +**Real-world context:** + +This is one of the most common ways secrets leak in production: +- Database connection failures during application startup +- Health check failures in container orchestration +- Development environments with verbose error reporting +- CI/CD pipelines where database connections fail + +**Remember:** The goal is to find the database password that gets exposed in the error message when the connection fails. \ No newline at end of file diff --git a/src/main/resources/explanations/challenge58_reason.adoc b/src/main/resources/explanations/challenge58_reason.adoc new file mode 100644 index 000000000..819590381 --- /dev/null +++ b/src/main/resources/explanations/challenge58_reason.adoc @@ -0,0 +1,40 @@ +=== Why Challenge 58 Matters: Database Connection String Exposure + +**The Problem:** + +This challenge exposes one of the most frequent and dangerous secret leakage scenarios in real-world applications: **database connection strings with embedded credentials that get exposed through poor error handling**. + +**Why This Happens:** + +1. **Embedded Credentials:** Developers put usernames and passwords directly in connection strings instead of using environment variables or secret management systems +2. **Poor Error Handling:** Applications don't sanitize error messages before logging or displaying them +3. **Verbose Error Reporting:** Development practices leak into production with detailed error messages +4. **Wide Distribution:** Error messages reach logs, monitoring systems, error tracking tools, and sometimes even end users + +**Real-World Impact:** + +- **Production Database Compromises:** Exposed credentials lead to direct database access +- **Data Breaches:** Once database credentials are leaked, all stored data is at risk +- **Lateral Movement:** Database access can be used to pivot to other systems +- **Compliance Violations:** Exposed credentials violate security standards and regulations + +**Common Exposure Vectors:** + +- Application startup logs when database is unavailable +- Health check failures in Kubernetes/Docker environments +- CI/CD pipeline logs during deployment failures +- Error tracking services (Sentry, Rollbar, Bugsnag) +- CloudWatch, Azure Monitor, or Google Cloud Logging +- Support tickets when users report errors + +**Prevention:** + +1. **External Secret Management:** Use environment variables, AWS Secrets Manager, Azure Key Vault, or HashiCorp Vault +2. **Connection String Sanitization:** Strip credentials from error messages before logging +3. **Least Privilege Logging:** Don't log connection strings or technical details that could contain secrets +4. **Separate Database Users:** Use read-only users for applications that don't need write access +5. **Connection Pooling:** Use connection pooling libraries that handle credentials securely + +**The Bottom Line:** + +Database connection string exposure is preventable with proper secret management practices and careful error handling. This vulnerability type has led to countless production breaches and should never occur in well-designed applications. \ No newline at end of file diff --git a/src/main/resources/wrong-secrets-configuration.yaml b/src/main/resources/wrong-secrets-configuration.yaml index 0bc8a3097..afc69325d 100644 --- a/src/main/resources/wrong-secrets-configuration.yaml +++ b/src/main/resources/wrong-secrets-configuration.yaml @@ -889,6 +889,19 @@ configurations: reason: "explanations/challenge57_reason.adoc" environments: *all_envs difficulty: *normal + category: *ai + ctf: + enabled: true + + - name: Challenge 58 + short-name: "challenge-58" + sources: + - class-name: "org.owasp.wrongsecrets.challenges.docker.Challenge58" + explanation: "explanations/challenge58.adoc" + hint: "explanations/challenge58_hint.adoc" + reason: "explanations/challenge58_reason.adoc" + environments: *all_envs + difficulty: *normal category: *logging ctf: enabled: true diff --git a/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57ControllerTest.java b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57ControllerTest.java index 9492f4760..304aea545 100644 --- a/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57ControllerTest.java +++ b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57ControllerTest.java @@ -16,16 +16,28 @@ class Challenge57ControllerTest { @InjectMocks private Challenge57Controller controller; @Test - void triggerDatabaseErrorShouldReturnErrorMessage() { + void getLLMJavaScriptShouldReturnJavaScriptCode() { // Given - String expectedError = "Database connection failed with connection string: ..."; - org.mockito.Mockito.when(challenge.simulateDatabaseConnectionError()).thenReturn(expectedError); + String expectedJs = "// AI Chat Application - Client-side JavaScript\nclass LLMChatApp {"; + org.mockito.Mockito.when(challenge.getLLMJavaScriptCode()).thenReturn(expectedJs); // When - String result = controller.triggerDatabaseError(); + String result = controller.getLLMJavaScript(); // Then - assertThat(result).isEqualTo(expectedError); - org.mockito.Mockito.verify(challenge).simulateDatabaseConnectionError(); + assertThat(result).isEqualTo(expectedJs); + org.mockito.Mockito.verify(challenge).getLLMJavaScriptCode(); + } + + @Test + void getLLMDemoPageShouldReturnHTMLPage() { + // When + String result = controller.getLLMDemoPage(); + + // Then + assertThat(result).contains(""); + assertThat(result).contains("LLM Chat Demo"); + assertThat(result).contains("Security Notice"); + assertThat(result).contains("/llm-chat.js"); } } diff --git a/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Test.java b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Test.java index 0ea33f215..a09d775b1 100644 --- a/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Test.java +++ b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Test.java @@ -10,13 +10,13 @@ class Challenge57Test { @Test void spoilerShouldReturnCorrectAnswer() { var challenge = new Challenge57(); - assertThat(challenge.spoiler()).isEqualTo(new Spoiler("SuperSecretDB2024!")); + assertThat(challenge.spoiler()).isEqualTo(new Spoiler("sk-llm-api-key-abc123def456ghi789jkl012mno345pqr678stu901vwx234yzA")); } @Test void answerCorrectShouldReturnTrueForCorrectAnswer() { var challenge = new Challenge57(); - assertThat(challenge.answerCorrect("SuperSecretDB2024!")).isTrue(); + assertThat(challenge.answerCorrect("sk-llm-api-key-abc123def456ghi789jkl012mno345pqr678stu901vwx234yzA")).isTrue(); } @Test @@ -30,18 +30,19 @@ void answerCorrectShouldReturnFalseForIncorrectAnswer() { @Test void answerCorrectShouldTrimWhitespace() { var challenge = new Challenge57(); - assertThat(challenge.answerCorrect(" SuperSecretDB2024! ")).isTrue(); + assertThat(challenge.answerCorrect(" sk-llm-api-key-abc123def456ghi789jkl012mno345pqr678stu901vwx234yzA ")).isTrue(); } @Test - void simulateDatabaseConnectionErrorShouldExposeConnectionString() { + void getLLMJavaScriptCodeShouldExposeAPIKey() { var challenge = new Challenge57(); - String errorMessage = challenge.simulateDatabaseConnectionError(); - - // Verify that the error message contains the exposed connection string - assertThat(errorMessage).contains("jdbc:postgresql://db.example.com:5432/userdb"); - assertThat(errorMessage).contains("user=dbadmin"); - assertThat(errorMessage).contains("password=SuperSecretDB2024!"); - assertThat(errorMessage).contains("Database connection failed"); + String jsCode = challenge.getLLMJavaScriptCode(); + + // Verify that the JavaScript code contains the exposed API key + assertThat(jsCode).contains("sk-llm-api-key-abc123def456ghi789jkl012mno345pqr678stu901vwx234yzA"); + assertThat(jsCode).contains("this.apiKey = "); + assertThat(jsCode).contains("console.log"); + assertThat(jsCode).contains("Authorization"); + assertThat(jsCode).contains("Bearer"); } } diff --git a/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge58ControllerTest.java b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge58ControllerTest.java new file mode 100644 index 000000000..f4674f596 --- /dev/null +++ b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge58ControllerTest.java @@ -0,0 +1,31 @@ +package org.owasp.wrongsecrets.challenges.docker; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class Challenge58ControllerTest { + + @Mock private Challenge58 challenge; + + @InjectMocks private Challenge58Controller controller; + + @Test + void triggerDatabaseErrorShouldReturnErrorMessage() { + // Given + String expectedError = "Database connection failed with connection string: ..."; + org.mockito.Mockito.when(challenge.simulateDatabaseConnectionError()).thenReturn(expectedError); + + // When + String result = controller.triggerDatabaseError(); + + // Then + assertThat(result).isEqualTo(expectedError); + org.mockito.Mockito.verify(challenge).simulateDatabaseConnectionError(); + } +} \ No newline at end of file diff --git a/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge58Test.java b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge58Test.java new file mode 100644 index 000000000..cc044ab49 --- /dev/null +++ b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge58Test.java @@ -0,0 +1,47 @@ +package org.owasp.wrongsecrets.challenges.docker; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.owasp.wrongsecrets.challenges.Spoiler; + +class Challenge58Test { + + @Test + void spoilerShouldReturnCorrectAnswer() { + var challenge = new Challenge58(); + assertThat(challenge.spoiler()).isEqualTo(new Spoiler("SuperSecretDB2024!")); + } + + @Test + void answerCorrectShouldReturnTrueForCorrectAnswer() { + var challenge = new Challenge58(); + assertThat(challenge.answerCorrect("SuperSecretDB2024!")).isTrue(); + } + + @Test + void answerCorrectShouldReturnFalseForIncorrectAnswer() { + var challenge = new Challenge58(); + assertThat(challenge.answerCorrect("wronganswer")).isFalse(); + assertThat(challenge.answerCorrect("")).isFalse(); + assertThat(challenge.answerCorrect(null)).isFalse(); + } + + @Test + void answerCorrectShouldTrimWhitespace() { + var challenge = new Challenge58(); + assertThat(challenge.answerCorrect(" SuperSecretDB2024! ")).isTrue(); + } + + @Test + void simulateDatabaseConnectionErrorShouldExposeConnectionString() { + var challenge = new Challenge58(); + String errorMessage = challenge.simulateDatabaseConnectionError(); + + // Verify that the error message contains the exposed connection string + assertThat(errorMessage).contains("jdbc:postgresql://db.example.com:5432/userdb"); + assertThat(errorMessage).contains("user=dbadmin"); + assertThat(errorMessage).contains("password=SuperSecretDB2024!"); + assertThat(errorMessage).contains("Database connection failed"); + } +} \ No newline at end of file diff --git a/static-site/pr-123/pages/about.html b/static-site/pr-123/pages/about.html new file mode 100644 index 000000000..e3ad99f48 --- /dev/null +++ b/static-site/pr-123/pages/about.html @@ -0,0 +1,436 @@ + + + +
+
+
📋 Static Preview Notice
+ This is a static preview of PR #123. Some dynamic content may be simplified or use mock data. +
+

About

+
+
+
+

About WrongSecrets

+

+ This app started as a bad example app for a talk for AllDayDevops in 2020, "DevSecOps — Our Secret + Management Journey from Code to Vault". How an organisation handles its secrets reflects its security maturity, + yet secrets management is not a "sexy" topic even within security. Many security breaches have their roots in improper management of secrets. + It turns out people do want some more guidance on it! +

+

+ Hence, we reworked the code base of this project and filed for it to become an OWASP project. + Our goal is to educate people about secrets management and its pitfalls while they have a good time learning! +

+ + + +

+ We hope you can better assess and implement proper secrets management after going through the challenges and explanations in our app. + Have fun, and remember to star us on GitHub! +

+

+ If you like WrongSecrets and its mission, please consider supporting OWASP in our name! +

+

Copyright (c) 2020-2025 Jeroen Willemsen and WrongSecrets contributors.

+

Licensed under AGPLv3

+
+
+
+
+

Licenses

+
+ The list below is generated with `mvn license:add-third-party` +
    + +
  • Lists of 362 third-party dependencies.
  • +
  • (Eclipse Public License - v 1.0) (GNU Lesser General Public License) Logback Classic Module (ch.qos.logback:logback-classic:1.5.18 - http://logback.qos.ch/logback-classic)
  • +
  • (Eclipse Public License - v 1.0) (GNU Lesser General Public License) Logback Core Module (ch.qos.logback:logback-core:1.5.18 - http://logback.qos.ch/logback-core)
  • +
  • (The MIT License (MIT)) Microsoft Azure Java Core Library (com.azure:azure-core:1.55.3 - https://github.com/Azure/azure-sdk-for-java)
  • +
  • (The MIT License (MIT)) Microsoft Azure Java Core AMQP Library (com.azure:azure-core-amqp:2.9.16 - https://github.com/Azure/azure-sdk-for-java)
  • +
  • (The MIT License (MIT)) Microsoft Azure Netty HTTP Client Library (com.azure:azure-core-http-netty:1.15.11 - https://github.com/Azure/azure-sdk-for-java)
  • +
  • (The MIT License (MIT)) Microsoft Azure Management Java Core Library (com.azure:azure-core-management:1.17.0 - https://github.com/Azure/azure-sdk-for-java)
  • +
  • (The MIT License (MIT)) Microsoft Azure client library for Identity (com.azure:azure-identity:1.15.4 - https://github.com/Azure/azure-sdk-for-java)
  • +
  • (The MIT License (MIT)) Microsoft Azure Java JSON Library (com.azure:azure-json:1.5.0 - https://github.com/Azure/azure-sdk-for-java)
  • +
  • (The MIT License (MIT)) Microsoft Azure client library for KeyVault Secrets (com.azure:azure-security-keyvault-secrets:4.9.4 - https://github.com/Azure/azure-sdk-for-java)
  • +
  • (The MIT License (MIT)) Microsoft Azure Java XML Library (com.azure:azure-xml:1.2.0 - https://github.com/Azure/azure-sdk-for-java)
  • +
  • (The MIT License (MIT)) Spring Cloud Azure AutoConfigure (com.azure.spring:spring-cloud-azure-autoconfigure:5.22.0 - https://microsoft.github.io/spring-cloud-azure)
  • +
  • (The MIT License (MIT)) Spring Cloud Azure Core (com.azure.spring:spring-cloud-azure-core:5.22.0 - https://microsoft.github.io/spring-cloud-azure)
  • +
  • (The MIT License (MIT)) Spring Cloud Azure Service (com.azure.spring:spring-cloud-azure-service:5.22.0 - https://microsoft.github.io/spring-cloud-azure)
  • +
  • (The MIT License (MIT)) Spring Cloud Azure Starter (com.azure.spring:spring-cloud-azure-starter:5.22.0 - https://microsoft.github.io/spring-cloud-azure)
  • +
  • (The MIT License (MIT)) Spring Cloud Azure Starter Key Vault Secrets (com.azure.spring:spring-cloud-azure-starter-keyvault-secrets:5.22.0 - https://microsoft.github.io/spring-cloud-azure)
  • +
  • (The Apache Software License, Version 2.0) Simple XML (safe) (com.carrotsearch.thirdparty:simple-xml-safe:2.7.1 - https://github.com/dweiss/simplexml)
  • +
  • (3-Clause BSD License) MinLog (com.esotericsoftware:minlog:1.3.1 - https://github.com/EsotericSoftware/minlog)
  • +
  • (Apache License, Version 2.0) Internet Time Utility (com.ethlo.time:itu:1.10.3 - https://github.com/ethlo/itu)
  • +
  • (The Apache Software License, Version 2.0) aalto-xml (com.fasterxml:aalto-xml:1.3.3 - https://github.com/FasterXML/aalto-xml)
  • +
  • (Apache License, Version 2.0) ClassMate (com.fasterxml:classmate:1.7.0 - https://github.com/FasterXML/java-classmate)
  • +
  • (The Apache Software License, Version 2.0) Jackson-annotations (com.fasterxml.jackson.core:jackson-annotations:2.19.1 - https://github.com/FasterXML/jackson)
  • +
  • (The Apache Software License, Version 2.0) Jackson-core (com.fasterxml.jackson.core:jackson-core:2.19.1 - https://github.com/FasterXML/jackson-core)
  • +
  • (The Apache Software License, Version 2.0) jackson-databind (com.fasterxml.jackson.core:jackson-databind:2.19.1 - https://github.com/FasterXML/jackson)
  • +
  • (The Apache Software License, Version 2.0) Jackson-dataformat-XML (com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.19.1 - https://github.com/FasterXML/jackson-dataformat-xml)
  • +
  • (The Apache Software License, Version 2.0) Jackson-dataformat-YAML (com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.19.1 - https://github.com/FasterXML/jackson-dataformats-text)
  • +
  • (The Apache Software License, Version 2.0) Jackson datatype: jdk8 (com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.19.1 - https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8)
  • +
  • (The Apache Software License, Version 2.0) Jackson datatype: JSR310 (com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.19.1 - https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jsr310)
  • +
  • (The Apache Software License, Version 2.0) Jackson module: Blackbird (com.fasterxml.jackson.module:jackson-module-blackbird:2.19.1 - https://github.com/FasterXML/jackson-modules-base)
  • +
  • (The Apache Software License, Version 2.0) Jackson-module-parameter-names (com.fasterxml.jackson.module:jackson-module-parameter-names:2.19.1 - https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names)
  • +
  • (The Apache License, Version 2.0) Woodstox (com.fasterxml.woodstox:woodstox-core:7.0.0 - https://github.com/FasterXML/woodstox)
  • +
  • (GNU Lesser General Public License version 3) (The Apache Software License, Version 2.0) jffi (com.github.jnr:jffi:1.3.13 - http://github.com/jnr/jffi)
  • +
  • (The Apache Software License, Version 2.0) jnr-a64asm (com.github.jnr:jnr-a64asm:1.0.0 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-a64asm)
  • +
  • (The Apache Software License, Version 2.0) jnr-constants (com.github.jnr:jnr-constants:0.10.4 - http://github.com/jnr/jnr-constants)
  • +
  • (The Apache Software License, Version 2.0) jnr-enxio (com.github.jnr:jnr-enxio:0.32.18 - http://github.com/jnr/jnr-enxio)
  • +
  • (The Apache Software License, Version 2.0) jnr-ffi (com.github.jnr:jnr-ffi:2.2.17 - http://github.com/jnr/jnr-ffi)
  • +
  • (The Apache Software License, Version 2.0) jnr-netdb (com.github.jnr:jnr-netdb:1.2.0 - http://github.com/jnr/jnr-netdb)
  • +
  • (Eclipse Public License - v 2.0) (GNU General Public License Version 2) (GNU Lesser General Public License Version 2.1) jnr-posix (com.github.jnr:jnr-posix:3.1.20 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix)
  • +
  • (The Apache Software License, Version 2.0) jnr-unixsocket (com.github.jnr:jnr-unixsocket:0.38.23 - http://github.com/jnr/jnr-unixsocket)
  • +
  • (MIT License) jnr-x86asm (com.github.jnr:jnr-x86asm:1.0.2 - http://github.com/jnr/jnr-x86asm)
  • +
  • (MIT) Package URL (com.github.package-url:packageurl-java:1.5.0 - https://github.com/package-url/packageurl-java)
  • +
  • (GNU LESSER GENERAL PUBLIC LICENSE, Version 2.1) SpotBugs Annotations (com.github.spotbugs:spotbugs-annotations:4.9.3 - https://spotbugs.github.io/)
  • +
  • (Apache License 2.0) compiler (com.github.spullara.mustache.java:compiler:0.9.6 - http://github.com/spullara/mustache.java)
  • +
  • (Apache License, Version 2.0) JCIP Annotations under Apache License (com.github.stephenc.jcip:jcip-annotations:1.0-1 - http://stephenc.github.com/jcip-annotations)
  • +
  • (Apache 2.0) Google Android Annotations Library (com.google.android:annotations:4.1.1.4 - http://source.android.com/)
  • +
  • (BSD-3-Clause) API Common (com.google.api:api-common:2.51.0 - https://github.com/googleapis/sdk-platform-java)
  • +
  • (BSD-3-Clause) GAX (Google Api eXtensions) for Java (Core) (com.google.api:gax:2.68.0 - https://github.com/googleapis/sdk-platform-java)
  • +
  • (BSD-3-Clause) GAX (Google Api eXtensions) for Java (gRPC) (com.google.api:gax-grpc:2.68.0 - https://github.com/googleapis/sdk-platform-java)
  • +
  • (BSD-3-Clause) GAX (Google Api eXtensions) for Java (HTTP JSON) (com.google.api:gax-httpjson:2.68.0 - https://github.com/googleapis/sdk-platform-java)
  • +
  • (Apache-2.0) proto-google-cloud-secretmanager-v1 (com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.67.0 - https://github.com/googleapis/google-cloud-java)
  • +
  • (Apache-2.0) proto-google-cloud-secretmanager-v1beta1 (com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:2.67.0 - https://github.com/googleapis/google-cloud-java)
  • +
  • (Apache-2.0) proto-google-cloud-secretmanager-v1beta2 (com.google.api.grpc:proto-google-cloud-secretmanager-v1beta2:2.67.0 - https://github.com/googleapis/google-cloud-java)
  • +
  • (Apache-2.0) proto-google-common-protos (com.google.api.grpc:proto-google-common-protos:2.59.0 - https://github.com/googleapis/sdk-platform-java)
  • +
  • (Apache-2.0) proto-google-iam-v1 (com.google.api.grpc:proto-google-iam-v1:1.54.0 - https://github.com/googleapis/sdk-platform-java)
  • +
  • (BSD New license) Google Auth Library for Java - Credentials (com.google.auth:google-auth-library-credentials:1.37.1 - https://github.com/googleapis/google-auth-library-java/google-auth-library-credentials)
  • +
  • (BSD New license) Google Auth Library for Java - OAuth2 HTTP (com.google.auth:google-auth-library-oauth2-http:1.37.1 - https://github.com/googleapis/google-auth-library-java/google-auth-library-oauth2-http)
  • +
  • (Apache 2.0) AutoValue Annotations (com.google.auto.value:auto-value-annotations:1.11.0 - https://github.com/google/auto/tree/main/value)
  • +
  • (Apache-2.0) Google Cloud Secret Manager (com.google.cloud:google-cloud-secretmanager:2.67.0 - https://github.com/googleapis/google-cloud-java)
  • +
  • (The Apache Software License, Version 2.0) FindBugs-jsr305 (com.google.code.findbugs:jsr305:3.0.2 - http://findbugs.sourceforge.net/)
  • +
  • (Apache-2.0) Gson (com.google.code.gson:gson:2.13.1 - https://github.com/google/gson)
  • +
  • (Apache 2.0) error-prone annotations (com.google.errorprone:error_prone_annotations:2.38.0 - https://errorprone.info/error_prone_annotations)
  • +
  • (The Apache Software License, Version 2.0) Guava InternalFutureFailureAccess and InternalFutures (com.google.guava:failureaccess:1.0.2 - https://github.com/google/guava/failureaccess)
  • +
  • (Apache License, Version 2.0) Guava: Google Core Libraries for Java (com.google.guava:guava:33.4.0-jre - https://github.com/google/guava)
  • +
  • (The Apache Software License, Version 2.0) Guava ListenableFuture only (com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava - https://github.com/google/guava/listenablefuture)
  • +
  • (The Apache Software License, Version 2.0) Google HTTP Client Library for Java (com.google.http-client:google-http-client:1.47.1 - https://github.com/googleapis/google-http-java-client/google-http-client)
  • +
  • (The Apache Software License, Version 2.0) GSON extensions to the Google HTTP Client Library for Java. (com.google.http-client:google-http-client-gson:1.47.1 - https://github.com/googleapis/google-http-java-client/google-http-client-gson)
  • +
  • (Apache License, Version 2.0) J2ObjC Annotations (com.google.j2objc:j2objc-annotations:3.0.0 - https://github.com/google/j2objc/)
  • +
  • (BSD-3-Clause) Protocol Buffers [Core] (com.google.protobuf:protobuf-java:4.29.4 - https://developers.google.com/protocol-buffers/protobuf-java/)
  • +
  • (BSD-3-Clause) Protocol Buffers [Util] (com.google.protobuf:protobuf-java-util:4.29.4 - https://developers.google.com/protocol-buffers/protobuf-java-util/)
  • +
  • (Go License) RE2/J (com.google.re2j:re2j:1.8 - http://github.com/google/re2j)
  • +
  • (EPL 1.0) (MPL 2.0) H2 Database Engine (com.h2database:h2:2.3.232 - https://h2database.com)
  • +
  • (The Apache Software License, Version 2.0) retirejs-core (com.h3xstream.retirejs:retirejs-core:3.0.4 - https://github.com/h3xstream/burp-retire-js/retirejs-core)
  • +
  • (Apache License Version 2.0) AhoCorasickDoubleArrayTrie (com.hankcs:aho-corasick-double-array-trie:1.2.3 - https://github.com/hankcs/AhoCorasickDoubleArrayTrie)
  • +
  • (The Apache Software License, Version 2.0) backport9 (com.headius:backport9:1.13 - http://nexus.sonatype.org/oss-repository-hosting.html/backport9)
  • +
  • (The Apache Software License, Version 2.0) invokebinder (com.headius:invokebinder:1.14 - http://maven.apache.org)
  • +
  • (The Apache Software License, Version 2.0) options (com.headius:options:1.6 - https://github.com/headius/options)
  • +
  • (MIT License) msal4j (com.microsoft.azure:msal4j:1.19.1 - https://github.com/AzureAD/microsoft-authentication-library-for-java)
  • +
  • (MIT License) msal4j-persistence-extension (com.microsoft.azure:msal4j-persistence-extension:1.3.0 - https://github.com/AzureAD/microsoft-authentication-library-for-java)
  • +
  • (The MIT License (MIT)) Extensions on Apache Proton-J library (com.microsoft.azure:qpid-proton-j-extensions:1.2.5 - https://github.com/Azure/qpid-proton-j-extensions)
  • +
  • (The MIT License) toml4j (com.moandjiezana.toml:toml4j:0.7.2 - http://moandjiezana.com/toml/toml4j)
  • +
  • (Apache License Version 2.0) JsonSchemaValidator (com.networknt:json-schema-validator:1.5.6 - https://github.com/networknt/json-schema-validator)
  • +
  • (The Apache Software License, Version 2.0) Nimbus Content Type (com.nimbusds:content-type:2.3 - https://bitbucket.org/connect2id/nimbus-content-type)
  • +
  • (The Apache Software License, Version 2.0) Nimbus LangTag (com.nimbusds:lang-tag:1.7 - https://bitbucket.org/connect2id/nimbus-language-tags)
  • +
  • (The Apache Software License, Version 2.0) Nimbus JOSE+JWT (com.nimbusds:nimbus-jose-jwt:10.0.1 - https://bitbucket.org/connect2id/nimbus-jose-jwt)
  • +
  • (Apache License, version 2.0) OAuth 2.0 SDK with OpenID Connect extensions (com.nimbusds:oauth2-oidc-sdk:11.23 - https://bitbucket.org/connect2id/oauth-2.0-sdk-with-openid-connect-extensions)
  • +
  • (The (New) BSD License) jmustache (com.samskivert:jmustache:1.16 - http://github.com/samskivert/jmustache)
  • +
  • (Eclipse Distribution License - v 1.0) Old JAXB Core (com.sun.xml.bind:jaxb-core:4.0.5 - https://eclipse-ee4j.github.io/jaxb-ri/)
  • +
  • (Eclipse Distribution License - v 1.0) Old JAXB Runtime (com.sun.xml.bind:jaxb-impl:4.0.5 - https://eclipse-ee4j.github.io/jaxb-ri/)
  • +
  • (Apache License 2.0) JSON library from Android SDK (com.vaadin.external.google:android-json:0.0.20131108.vaadin1 - http://developer.android.com/sdk)
  • +
  • (Apache-2.0) Apache Commons Codec (commons-codec:commons-codec:1.18.0 - https://commons.apache.org/proper/commons-codec/)
  • +
  • (Apache License, Version 2.0) Apache Commons Collections (commons-collections:commons-collections:3.2.2 - http://commons.apache.org/collections/)
  • +
  • (The Apache Software License, Version 2.0) Commons Digester (commons-digester:commons-digester:2.1 - http://commons.apache.org/digester/)
  • +
  • (Apache-2.0) Apache Commons IO (commons-io:commons-io:2.18.0 - https://commons.apache.org/proper/commons-io/)
  • +
  • (Apache-2.0) Apache Commons Logging (commons-logging:commons-logging:1.3.2 - https://commons.apache.org/proper/commons-logging/)
  • +
  • (Apache-2.0) Apache Commons Validator (commons-validator:commons-validator:1.9.0 - http://commons.apache.org/proper/commons-validator/)
  • +
  • (The Apache License, Version 2.0) jcs3-slf4j (io.github.jeremylong:jcs3-slf4j:1.0.5 - https://github.com/jeremylong/jcs3-slf4j/)
  • +
  • (The Apache License, Version 2.0) open-vulnerability-clients (io.github.jeremylong:open-vulnerability-clients:7.3.2 - https://github.com/jeremylong/vuln-tools/)
  • +
  • (Apache 2.0) io.grpc:grpc-alts (io.grpc:grpc-alts:1.71.0 - https://github.com/grpc/grpc-java)
  • +
  • (Apache 2.0) io.grpc:grpc-api (io.grpc:grpc-api:1.71.0 - https://github.com/grpc/grpc-java)
  • +
  • (Apache 2.0) io.grpc:grpc-auth (io.grpc:grpc-auth:1.71.0 - https://github.com/grpc/grpc-java)
  • +
  • (Apache 2.0) io.grpc:grpc-context (io.grpc:grpc-context:1.71.0 - https://github.com/grpc/grpc-java)
  • +
  • (Apache 2.0) io.grpc:grpc-core (io.grpc:grpc-core:1.71.0 - https://github.com/grpc/grpc-java)
  • +
  • (Apache 2.0) io.grpc:grpc-googleapis (io.grpc:grpc-googleapis:1.71.0 - https://github.com/grpc/grpc-java)
  • +
  • (Apache 2.0) io.grpc:grpc-grpclb (io.grpc:grpc-grpclb:1.71.0 - https://github.com/grpc/grpc-java)
  • +
  • (Apache 2.0) io.grpc:grpc-inprocess (io.grpc:grpc-inprocess:1.71.0 - https://github.com/grpc/grpc-java)
  • +
  • (Apache 2.0) io.grpc:grpc-netty-shaded (io.grpc:grpc-netty-shaded:1.71.0 - https://github.com/grpc/grpc-java)
  • +
  • (Apache 2.0) io.grpc:grpc-protobuf (io.grpc:grpc-protobuf:1.71.0 - https://github.com/grpc/grpc-java)
  • +
  • (Apache 2.0) io.grpc:grpc-protobuf-lite (io.grpc:grpc-protobuf-lite:1.71.0 - https://github.com/grpc/grpc-java)
  • +
  • (Apache 2.0) io.grpc:grpc-services (io.grpc:grpc-services:1.71.0 - https://github.com/grpc/grpc-java)
  • +
  • (Apache 2.0) io.grpc:grpc-stub (io.grpc:grpc-stub:1.71.0 - https://github.com/grpc/grpc-java)
  • +
  • (Apache 2.0) io.grpc:grpc-util (io.grpc:grpc-util:1.71.0 - https://github.com/grpc/grpc-java)
  • +
  • (Apache 2.0) io.grpc:grpc-xds (io.grpc:grpc-xds:1.71.0 - https://github.com/grpc/grpc-java)
  • +
  • (The Apache Software License, Version 2.0) micrometer-commons (io.micrometer:micrometer-commons:1.15.1 - https://github.com/micrometer-metrics/micrometer)
  • +
  • (The Apache Software License, Version 2.0) micrometer-core (io.micrometer:micrometer-core:1.15.1 - https://github.com/micrometer-metrics/micrometer)
  • +
  • (The Apache Software License, Version 2.0) micrometer-jakarta9 (io.micrometer:micrometer-jakarta9:1.15.1 - https://github.com/micrometer-metrics/micrometer)
  • +
  • (The Apache Software License, Version 2.0) micrometer-observation (io.micrometer:micrometer-observation:1.15.1 - https://github.com/micrometer-metrics/micrometer)
  • +
  • (Apache License, Version 2.0) Netty/Buffer (io.netty:netty-buffer:4.1.118.Final - https://netty.io/netty-buffer/)
  • +
  • (Apache License, Version 2.0) Netty/Codec (io.netty:netty-codec:4.1.118.Final - https://netty.io/netty-codec/)
  • +
  • (Apache License, Version 2.0) Netty/Codec/DNS (io.netty:netty-codec-dns:4.1.118.Final - https://netty.io/netty-codec-dns/)
  • +
  • (Apache License, Version 2.0) Netty/Codec/HTTP (io.netty:netty-codec-http:4.1.118.Final - https://netty.io/netty-codec-http/)
  • +
  • (Apache License, Version 2.0) Netty/Codec/HTTP2 (io.netty:netty-codec-http2:4.1.118.Final - https://netty.io/netty-codec-http2/)
  • +
  • (Apache License, Version 2.0) Netty/Codec/Socks (io.netty:netty-codec-socks:4.1.118.Final - https://netty.io/netty-codec-socks/)
  • +
  • (Apache License, Version 2.0) Netty/Common (io.netty:netty-common:4.1.118.Final - https://netty.io/netty-common/)
  • +
  • (Apache License, Version 2.0) Netty/Handler (io.netty:netty-handler:4.1.118.Final - https://netty.io/netty-handler/)
  • +
  • (Apache License, Version 2.0) Netty/Handler/Proxy (io.netty:netty-handler-proxy:4.1.118.Final - https://netty.io/netty-handler-proxy/)
  • +
  • (Apache License, Version 2.0) Netty/Resolver (io.netty:netty-resolver:4.1.118.Final - https://netty.io/netty-resolver/)
  • +
  • (Apache License, Version 2.0) Netty/Resolver/DNS (io.netty:netty-resolver-dns:4.1.118.Final - https://netty.io/netty-resolver-dns/)
  • +
  • (Apache License, Version 2.0) Netty/Resolver/DNS/Classes/MacOS (io.netty:netty-resolver-dns-classes-macos:4.1.118.Final - https://netty.io/netty-resolver-dns-classes-macos/)
  • +
  • (Apache License, Version 2.0) Netty/Resolver/DNS/Native/MacOS (io.netty:netty-resolver-dns-native-macos:4.1.118.Final - https://netty.io/netty-resolver-dns-native-macos/)
  • +
  • (The Apache Software License, Version 2.0) Netty/TomcatNative [BoringSSL - Static] (io.netty:netty-tcnative-boringssl-static:2.0.70.Final - https://github.com/netty/netty-tcnative/netty-tcnative-boringssl-static/)
  • +
  • (The Apache Software License, Version 2.0) Netty/TomcatNative [OpenSSL - Classes] (io.netty:netty-tcnative-classes:2.0.70.Final - https://github.com/netty/netty-tcnative/netty-tcnative-classes/)
  • +
  • (Apache License, Version 2.0) Netty/Transport (io.netty:netty-transport:4.1.118.Final - https://netty.io/netty-transport/)
  • +
  • (Apache License, Version 2.0) Netty/Transport/Classes/Epoll (io.netty:netty-transport-classes-epoll:4.1.118.Final - https://netty.io/netty-transport-classes-epoll/)
  • +
  • (Apache License, Version 2.0) Netty/Transport/Classes/KQueue (io.netty:netty-transport-classes-kqueue:4.1.118.Final - https://netty.io/netty-transport-classes-kqueue/)
  • +
  • (Apache License, Version 2.0) Netty/Transport/Native/Epoll (io.netty:netty-transport-native-epoll:4.1.118.Final - https://netty.io/netty-transport-native-epoll/)
  • +
  • (Apache License, Version 2.0) Netty/Transport/Native/KQueue (io.netty:netty-transport-native-kqueue:4.1.118.Final - https://netty.io/netty-transport-native-kqueue/)
  • +
  • (Apache License, Version 2.0) Netty/Transport/Native/Unix/Common (io.netty:netty-transport-native-unix-common:4.1.118.Final - https://netty.io/netty-transport-native-unix-common/)
  • +
  • (The Apache License, Version 2.0) OpenCensus (io.opencensus:opencensus-api:0.31.1 - https://github.com/census-instrumentation/opencensus-java)
  • +
  • (The Apache License, Version 2.0) OpenCensus (io.opencensus:opencensus-contrib-http-util:0.31.1 - https://github.com/census-instrumentation/opencensus-java)
  • +
  • (Apache 2.0) perfmark:perfmark-api (io.perfmark:perfmark-api:0.27.0 - https://github.com/perfmark/perfmark)
  • +
  • (Apache License, Version 2.0) Non-Blocking Reactive Foundation for the JVM (io.projectreactor:reactor-core:3.7.7 - https://github.com/reactor/reactor-core)
  • +
  • (The Apache Software License, Version 2.0) Core functionality for the Reactor Netty library (io.projectreactor.netty:reactor-netty-core:1.2.7 - https://github.com/reactor/reactor-netty)
  • +
  • (The Apache Software License, Version 2.0) HTTP functionality for the Reactor Netty library (io.projectreactor.netty:reactor-netty-http:1.2.7 - https://github.com/reactor/reactor-netty)
  • +
  • (Apache License 2.0) swagger-annotations-jakarta (io.swagger.core.v3:swagger-annotations-jakarta:2.2.30 - https://github.com/swagger-api/swagger-core/modules/swagger-annotations-jakarta)
  • +
  • (Apache License 2.0) swagger-core-jakarta (io.swagger.core.v3:swagger-core-jakarta:2.2.30 - https://github.com/swagger-api/swagger-core/modules/swagger-core-jakarta)
  • +
  • (Apache License 2.0) swagger-models-jakarta (io.swagger.core.v3:swagger-models-jakarta:2.2.30 - https://github.com/swagger-api/swagger-core/modules/swagger-models-jakarta)
  • +
  • (EDL 1.0) Jakarta Activation API (jakarta.activation:jakarta.activation-api:2.1.3 - https://github.com/jakartaee/jaf-api)
  • +
  • (EPL 2.0) (GPL2 w/ CPE) Jakarta Annotations API (jakarta.annotation:jakarta.annotation-api:2.1.1 - https://projects.eclipse.org/projects/ee4j.ca)
  • +
  • (EPL 2.0) (GPL2 w/ CPE) jakarta.transaction API (jakarta.transaction:jakarta.transaction-api:2.0.1 - https://projects.eclipse.org/projects/ee4j.jta)
  • +
  • (Apache License 2.0) Jakarta Bean Validation API (jakarta.validation:jakarta.validation-api:3.0.2 - https://beanvalidation.org)
  • +
  • (Eclipse Distribution License - v 1.0) Jakarta XML Binding API (jakarta.xml.bind:jakarta.xml.bind-api:4.0.2 - https://github.com/jakartaee/jaxb-api/jakarta.xml.bind-api)
  • +
  • (CDDL/GPLv2+CE) JavaBeans Activation Framework API jar (javax.activation:javax.activation-api:1.2.0 - http://java.net/all/javax.activation-api/)
  • +
  • (CDDL + GPLv2 with classpath exception) javax.annotation API (javax.annotation:javax.annotation-api:1.3.2 - http://jcp.org/en/jsr/detail?id=250)
  • +
  • (The Apache Software License, Version 2.0) javax.inject (javax.inject:javax.inject:1 - http://code.google.com/p/atinject/)
  • +
  • (CDDL 1.1) (GPL2 w/ CPE) javax.ws.rs-api (javax.ws.rs:javax.ws.rs-api:2.0.1 - http://jax-rs-spec.java.net)
  • +
  • (CDDL 1.1) (GPL2 w/ CPE) jaxb-api (javax.xml.bind:jaxb-api:2.3.1 - https://github.com/javaee/jaxb-spec/jaxb-api)
  • +
  • (Apache License, Version 2.0) Joda-Time (joda-time:joda-time:2.12.7 - https://www.joda.org/joda-time/)
  • +
  • (Eclipse Public License 1.0) JUnit (junit:junit:4.13.2 - http://junit.org)
  • +
  • (The Apache Software License, Version 2.0) jitescript (me.qmx.jitescript:jitescript:0.4.1 - https://github.com/qmx/jitescript)
  • +
  • (Apache-2.0) (LGPL-2.1-or-later) Java Native Access (net.java.dev.jna:jna:5.13.0 - https://github.com/java-native-access/jna)
  • +
  • (Apache-2.0) (LGPL-2.1-or-later) Java Native Access Platform (net.java.dev.jna:jna-platform:5.13.0 - https://github.com/java-native-access/jna)
  • +
  • (The Apache Software License, Version 2.0) ASM based accessors helper used by json-smart (net.minidev:accessors-smart:2.5.2 - https://urielch.github.io/)
  • +
  • (The Apache Software License, Version 2.0) JSON Small and Fast Parser (net.minidev:json-smart:2.5.2 - https://urielch.github.io/)
  • +
  • (The Apache Software License, Version 2.0) groovy-extensions (nz.net.ultraq.groovy:groovy-extensions:2.3.3 - https://github.com/ultraq/groovy-extensions/)
  • +
  • (The Apache Software License, Version 2.0) thymeleaf-expression-processor (nz.net.ultraq.thymeleaf:thymeleaf-expression-processor:3.2.0 - https://github.com/ultraq/thymeleaf-expression-processor/)
  • +
  • (The Apache Software License, Version 2.0) thymeleaf-layout-dialect (nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.4.0 - https://github.com/ultraq/thymeleaf-layout-dialect/)
  • +
  • (The Apache Software License, Version 2.0) OGNL - Object Graph Navigation Library (ognl:ognl:3.3.4 - https://github.com/jkuhnert/ognl/)
  • +
  • (Apache-2.0) jdiagnostics (org.anarres.jdiagnostics:jdiagnostics:1.0.7 - https://github.com/shevek/jdiagnostics)
  • +
  • (Apache License, Version 2.0) Apache Commons Collections (org.apache.commons:commons-collections4:4.4 - https://commons.apache.org/proper/commons-collections/)
  • +
  • (Apache-2.0) Apache Commons Compress (org.apache.commons:commons-compress:1.24.0 - https://commons.apache.org/proper/commons-compress/)
  • +
  • (Apache-2.0) Apache Commons DBCP (org.apache.commons:commons-dbcp2:2.13.0 - https://commons.apache.org/proper/commons-dbcp/)
  • +
  • (Apache-2.0) Apache Commons JCS :: Core (org.apache.commons:commons-jcs3-core:3.2.1 - http://commons.apache.org/proper/commons-jcs/commons-jcs3-core/)
  • +
  • (Apache-2.0) Apache Commons Lang (org.apache.commons:commons-lang3:3.17.0 - https://commons.apache.org/proper/commons-lang/)
  • +
  • (Apache-2.0) Apache Commons Pool (org.apache.commons:commons-pool2:2.12.1 - https://commons.apache.org/proper/commons-pool/)
  • +
  • (Apache-2.0) Apache Commons Text (org.apache.commons:commons-text:1.13.1 - https://commons.apache.org/proper/commons-text)
  • +
  • (The Apache Software License, Version 2.0) Apache Groovy (org.apache.groovy:groovy:4.0.27 - https://groovy-lang.org)
  • +
  • (Apache License, Version 2.0) Apache HttpClient (org.apache.httpcomponents:httpclient:4.5.14 - http://hc.apache.org/httpcomponents-client-ga)
  • +
  • (Apache License, Version 2.0) Apache HttpCore (org.apache.httpcomponents:httpcore:4.4.16 - http://hc.apache.org/httpcomponents-core-ga)
  • +
  • (Apache License, Version 2.0) Apache HttpClient (org.apache.httpcomponents.client5:httpclient5:5.5 - https://hc.apache.org/httpcomponents-client-5.5.x/5.5/httpclient5/)
  • +
  • (Apache License, Version 2.0) Apache HttpClient Cache (org.apache.httpcomponents.client5:httpclient5-cache:5.5 - https://hc.apache.org/httpcomponents-client-5.5.x/5.5/httpclient5-cache/)
  • +
  • (Apache License, Version 2.0) Apache HttpComponents Core HTTP/1.1 (org.apache.httpcomponents.core5:httpcore5:5.3.4 - https://hc.apache.org/httpcomponents-core-5.3.x/5.3.4/httpcore5/)
  • +
  • (Apache License, Version 2.0) Apache HttpComponents Core HTTP/2 (org.apache.httpcomponents.core5:httpcore5-h2:5.3.4 - https://hc.apache.org/httpcomponents-core-5.3.x/5.3.4/httpcore5-h2/)
  • +
  • (Apache-2.0) Apache Log4j API (org.apache.logging.log4j:log4j-api:2.24.3 - https://logging.apache.org/log4j/2.x/log4j/log4j-api/)
  • +
  • (Apache-2.0) Log4j API to SLF4J Adapter (org.apache.logging.log4j:log4j-to-slf4j:2.24.3 - https://logging.apache.org/log4j/2.x/log4j/log4j-to-slf4j/)
  • +
  • (Apache 2) Apache Lucene (module: common) (org.apache.lucene:lucene-analysis-common:9.12.0 - https://lucene.apache.org/)
  • +
  • (Apache 2) Apache Lucene (module: core) (org.apache.lucene:lucene-core:9.12.0 - https://lucene.apache.org/)
  • +
  • (Apache 2) Apache Lucene (module: facet) (org.apache.lucene:lucene-facet:9.12.0 - https://lucene.apache.org/)
  • +
  • (Apache 2) Apache Lucene (module: queries) (org.apache.lucene:lucene-queries:9.12.0 - https://lucene.apache.org/)
  • +
  • (Apache 2) Apache Lucene (module: queryparser) (org.apache.lucene:lucene-queryparser:9.12.0 - https://lucene.apache.org/)
  • +
  • (Apache 2) Apache Lucene (module: sandbox) (org.apache.lucene:lucene-sandbox:9.12.0 - https://lucene.apache.org/)
  • +
  • (The Apache Software License, Version 2.0) Maven Aether Provider (org.apache.maven:maven-aether-provider:3.0 - http://maven.apache.org/maven-aether-provider/)
  • +
  • (The Apache Software License, Version 2.0) Maven Artifact (org.apache.maven:maven-artifact:3.0 - http://maven.apache.org/maven-artifact/)
  • +
  • (The Apache Software License, Version 2.0) Maven Core (org.apache.maven:maven-core:3.0 - http://maven.apache.org/maven-core/)
  • +
  • (The Apache Software License, Version 2.0) Maven Model (org.apache.maven:maven-model:3.0 - http://maven.apache.org/maven-model/)
  • +
  • (The Apache Software License, Version 2.0) Maven Model Builder (org.apache.maven:maven-model-builder:3.0 - http://maven.apache.org/maven-model-builder/)
  • +
  • (The Apache Software License, Version 2.0) Maven Plugin API (org.apache.maven:maven-plugin-api:3.0 - http://maven.apache.org/maven-plugin-api/)
  • +
  • (The Apache Software License, Version 2.0) Maven Repository Metadata Model (org.apache.maven:maven-repository-metadata:3.0 - http://maven.apache.org/maven-repository-metadata/)
  • +
  • (The Apache Software License, Version 2.0) Maven Settings (org.apache.maven:maven-settings:3.0 - http://maven.apache.org/maven-settings/)
  • +
  • (The Apache Software License, Version 2.0) Maven Settings Builder (org.apache.maven:maven-settings-builder:3.0 - http://maven.apache.org/maven-settings-builder/)
  • +
  • (Apache-2.0) Doxia :: Sink API (org.apache.maven.doxia:doxia-sink-api:2.0.0 - https://maven.apache.org/doxia/doxia/doxia-sink-api/)
  • +
  • (Apache-2.0) Apache Maven Reporting API (org.apache.maven.reporting:maven-reporting-api:4.0.0 - https://maven.apache.org/shared/maven-reporting-api/)
  • +
  • (Apache License, Version 2.0) Maven Artifact Resolver API (org.apache.maven.resolver:maven-resolver-api:1.4.1 - https://maven.apache.org/resolver/maven-resolver-api/)
  • +
  • (Apache License, Version 2.0) Maven Artifact Resolver Utilities (org.apache.maven.resolver:maven-resolver-util:1.4.1 - https://maven.apache.org/resolver/maven-resolver-util/)
  • +
  • (Apache-2.0) Apache Maven File Management API (org.apache.maven.shared:file-management:3.2.0 - https://maven.apache.org/shared/file-management/)
  • +
  • (Apache License, Version 2.0) Apache Maven Artifact Transfer (org.apache.maven.shared:maven-artifact-transfer:0.13.1 - https://maven.apache.org/shared/maven-artifact-transfer/)
  • +
  • (Apache-2.0) Apache Maven Common Artifact Filters (org.apache.maven.shared:maven-common-artifact-filters:3.4.0 - https://maven.apache.org/shared/maven-common-artifact-filters/)
  • +
  • (Apache-2.0) Apache Maven Dependency Tree (org.apache.maven.shared:maven-dependency-tree:3.3.0 - https://maven.apache.org/shared/maven-dependency-tree/)
  • +
  • (Apache License, Version 2.0) Proton-J (org.apache.qpid:proton-j:0.34.1 - https://qpid.apache.org/proton/proton-j)
  • +
  • (Apache License, Version 2.0) tomcat-embed-core (org.apache.tomcat.embed:tomcat-embed-core:10.1.42 - https://tomcat.apache.org/)
  • +
  • (Apache License, Version 2.0) tomcat-embed-el (org.apache.tomcat.embed:tomcat-embed-el:10.1.42 - https://tomcat.apache.org/)
  • +
  • (Apache License, Version 2.0) tomcat-embed-websocket (org.apache.tomcat.embed:tomcat-embed-websocket:10.1.42 - https://tomcat.apache.org/)
  • +
  • (Apache-2.0) Apache Velocity - Engine (org.apache.velocity:velocity-engine-core:2.4.1 - http://velocity.apache.org/engine/devel/velocity-engine-core/)
  • +
  • (The Apache Software License, Version 2.0) asciidoctorj (org.asciidoctor:asciidoctorj:3.0.0 - https://github.com/asciidoctor/asciidoctorj)
  • +
  • (The Apache Software License, Version 2.0) asciidoctorj-api (org.asciidoctor:asciidoctorj-api:3.0.0 - https://github.com/asciidoctor/asciidoctorj)
  • +
  • (The Apache Software License, Version 2.0) attoparser (org.attoparser:attoparser:2.0.7.RELEASE - https://www.attoparser.org)
  • +
  • (Apache Software License, Version 2.0) (Bouncy Castle Licence) Bouncy Castle OpenPGP API (org.bouncycastle:bcpg-jdk18on:1.78 - https://www.bouncycastle.org/java.html)
  • +
  • (Bouncy Castle Licence) Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs (org.bouncycastle:bcpkix-jdk18on:1.78.1 - https://www.bouncycastle.org/java.html)
  • +
  • (Bouncy Castle Licence) Bouncy Castle Provider (org.bouncycastle:bcprov-jdk18on:1.80 - https://www.bouncycastle.org/download/bouncy-castle-java/)
  • +
  • (Bouncy Castle Licence) Bouncy Castle ASN.1 Extension and Utility APIs (org.bouncycastle:bcutil-jdk18on:1.78.1 - https://www.bouncycastle.org/java.html)
  • +
  • (The MIT License) Checker Qual (org.checkerframework:checker-qual:3.49.0 - https://checkerframework.org/)
  • +
  • (MIT license) Animal Sniffer Annotations (org.codehaus.mojo:animal-sniffer-annotations:1.24 - https://www.mojohaus.org/animal-sniffer/animal-sniffer-annotations)
  • +
  • (The Apache Software License, Version 2.0) Plexus Classworlds (org.codehaus.plexus:plexus-classworlds:2.2.3 - http://plexus.codehaus.org/plexus-classworlds/)
  • +
  • (Apache License, Version 2.0) Plexus :: Component Annotations (org.codehaus.plexus:plexus-component-annotations:2.0.0 - http://codehaus-plexus.github.io/plexus-containers/plexus-component-annotations/)
  • +
  • (The Apache Software License, Version 2.0) Plexus Interpolation API (org.codehaus.plexus:plexus-interpolation:1.14 - http://plexus.codehaus.org/plexus-components/plexus-interpolation)
  • +
  • (Apache License, Version 2.0) Plexus Common Utilities (org.codehaus.plexus:plexus-utils:4.0.2 - https://codehaus-plexus.github.io/plexus-utils/)
  • +
  • (Apache License, Version 2.0) Plexus XML Utilities (org.codehaus.plexus:plexus-xml:3.0.1 - https://codehaus-plexus.github.io/plexus-xml/)
  • +
  • (The BSD 2-Clause License) Stax2 API (org.codehaus.woodstox:stax2-api:4.2.2 - http://github.com/FasterXML/stax2-api)
  • +
  • (Apache 2) org.conscrypt:conscrypt-openjdk-uber (org.conscrypt:conscrypt-openjdk-uber:2.5.2 - https://conscrypt.org/)
  • +
  • (BSD-2-Clause) crac (org.crac:crac:1.5.0 - https://github.com/crac/org.crac)
  • +
  • (Apache-2.0) CycloneDX Core (Java) (org.cyclonedx:cyclonedx-core-java:10.2.1 - https://github.com/CycloneDX/cyclonedx-core-java)
  • +
  • (EDL 1.0) Angus Activation Registries (org.eclipse.angus:angus-activation:2.0.2 - https://github.com/eclipse-ee4j/angus-activation/angus-activation)
  • +
  • (Eclipse Public License - Version 2.0) Eclipse Packager :: Core (org.eclipse.packager:packager-core:0.21.0 - https://eclipse.org/packager/packager-core)
  • +
  • (Eclipse Public License - Version 2.0) Eclipse Packager :: RPM (org.eclipse.packager:packager-rpm:0.21.0 - https://eclipse.org/packager/packager-rpm)
  • +
  • (Eclipse Public License 2.0) (GNU General Public License, version 2 with the GNU Classpath Exception) JSON-P Default Provider (org.glassfish:jakarta.json:2.0.1 - https://github.com/eclipse-ee4j/jsonp)
  • +
  • (BSD-3-Clause) Hamcrest (org.hamcrest:hamcrest:3.0 - http://hamcrest.org/JavaHamcrest/)
  • +
  • (BSD-3-Clause) Hamcrest Core (org.hamcrest:hamcrest-core:3.0 - http://hamcrest.org/JavaHamcrest/)
  • +
  • (BSD-2-Clause) (Public Domain, per Creative Commons CC0) HdrHistogram (org.hdrhistogram:HdrHistogram:2.2.2 - http://hdrhistogram.github.io/HdrHistogram/)
  • +
  • (Apache License 2.0) Hibernate Validator Engine (org.hibernate.validator:hibernate-validator:8.0.2.Final - http://hibernate.org/validator/hibernate-validator)
  • +
  • (Apache License 2.0) (LGPL 2.1) (MPL 1.1) Javassist (org.javassist:javassist:3.29.0-GA - http://www.javassist.org/)
  • +
  • (Apache License 2.0) JBoss Logging 3 (org.jboss.logging:jboss-logging:3.6.1.Final - http://www.jboss.org)
  • +
  • (EPL) Dirgra (org.jruby:dirgra:0.5 - https://github.com/jruby/dirgra)
  • +
  • (EPL-2.0) (GPL-2.0) (LGPL-2.1) JRuby Main Maven Artifact (org.jruby:jruby:10.0.0.1 - https://github.com/jruby/jruby/jruby-artifacts/jruby)
  • +
  • (EPL-2.0) (GPL-2.0) (LGPL-2.1) JRuby Base (org.jruby:jruby-base:10.0.0.1 - https://github.com/jruby/jruby/jruby-base)
  • +
  • (EPL-2.0) (GPL-2.0) (LGPL-2.1) JRuby Complete (org.jruby:jruby-complete:10.0.0.1 - https://github.com/jruby/jruby/jruby-artifacts/jruby-complete)
  • +
  • (EPL-2.0) (GPL-2.0) (LGPL-2.1) JRuby Lib Setup (org.jruby:jruby-stdlib:10.0.0.1 - https://github.com/jruby/jruby/jruby-stdlib)
  • +
  • (BSD) JZlib (org.jruby:jzlib:1.1.5 - http://www.jcraft.com/jzlib/)
  • +
  • (MIT License) JCodings (org.jruby.jcodings:jcodings:1.0.63 - http://nexus.sonatype.org/oss-repository-hosting.html/jcodings)
  • +
  • (MIT License) Joni (org.jruby.joni:joni:2.2.6 - http://nexus.sonatype.org/oss-repository-hosting.html/joni)
  • +
  • (The MIT License) jsoup Java HTML Parser (org.jsoup:jsoup:1.20.1 - https://jsoup.org/)
  • +
  • (The Apache License, Version 2.0) JSpecify annotations (org.jspecify:jspecify:1.0.0 - http://jspecify.org/)
  • +
  • (Public Domain, per Creative Commons CC0) LatencyUtils (org.latencyutils:LatencyUtils:2.0.3 - http://latencyutils.github.io/LatencyUtils/)
  • +
  • (Apache License, Version 2.0) KeePassJava2 :: All (org.linguafranca.pwdb:KeePassJava2:2.2.4 - https://github.com/jorabin/KeePassJava2/KeePassJava2)
  • +
  • (Apache License, Version 2.0) KeePassJava2 :: DOM (org.linguafranca.pwdb:KeePassJava2-dom:2.2.4 - https://github.com/jorabin/KeePassJava2/KeePassJava2-dom)
  • +
  • (Apache License, Version 2.0) KeePassJava2 :: Jackson (org.linguafranca.pwdb:KeePassJava2-jackson:2.2.4 - https://github.com/jorabin/KeePassJava2/KeePassJava2-jackson)
  • +
  • (Apache License, Version 2.0) KeePassJava2 :: JAXB (org.linguafranca.pwdb:KeePassJava2-jaxb:2.2.4 - https://github.com/jorabin/KeePassJava2/KeePassJava2-jaxb)
  • +
  • (Apache License, Version 2.0) KeePassJava2 :: KDB (org.linguafranca.pwdb:KeePassJava2-kdb:2.2.4 - https://github.com/jorabin/KeePassJava2/KeePassJava2-kdb)
  • +
  • (Apache License, Version 2.0) KeePassJava2 :: KDBX (org.linguafranca.pwdb:KeePassJava2-kdbx:2.2.4 - https://github.com/jorabin/KeePassJava2/KeePassJava2-kdbx)
  • +
  • (Apache License, Version 2.0) KeePassJava2 :: Simple (org.linguafranca.pwdb:KeePassJava2-simple:2.2.4 - https://github.com/jorabin/KeePassJava2/KeePassJava2-simple)
  • +
  • (Apache License, Version 2.0) PWDB :: Database (org.linguafranca.pwdb:database:2.2.4 - https://github.com/jorabin/KeePassJava2/database)
  • +
  • (Apache License, Version 2.0) PWDB :: Util (org.linguafranca.pwdb:util:2.2.4 - https://github.com/jorabin/KeePassJava2/util)
  • +
  • (BSD-3-Clause) asm (org.ow2.asm:asm:9.7.1 - http://asm.ow2.io/)
  • +
  • (BSD-3-Clause) asm-analysis (org.ow2.asm:asm-analysis:9.7.1 - http://asm.ow2.io/)
  • +
  • (BSD-3-Clause) asm-commons (org.ow2.asm:asm-commons:9.7.1 - http://asm.ow2.io/)
  • +
  • (BSD-3-Clause) asm-tree (org.ow2.asm:asm-tree:9.7.1 - http://asm.ow2.io/)
  • +
  • (BSD-3-Clause) asm-util (org.ow2.asm:asm-util:9.7.1 - http://asm.ow2.io/)
  • +
  • (The Apache Software License, Version 2.0) Dependency-Check Core (org.owasp:dependency-check-core:12.1.3 - https://github.com/dependency-check/DependencyCheck.git/dependency-check-core)
  • +
  • (The Apache Software License, Version 2.0) Dependency-Check Maven Plugin (org.owasp:dependency-check-maven:12.1.3 - https://github.com/dependency-check/DependencyCheck.git/dependency-check-maven)
  • +
  • (The Apache Software License, Version 2.0) Dependency-Check Utils (org.owasp:dependency-check-utils:12.1.3 - https://github.com/dependency-check/DependencyCheck.git/dependency-check-utils)
  • +
  • (The MIT License) Project Lombok (org.projectlombok:lombok:1.18.38 - https://projectlombok.org)
  • +
  • (MIT-0) reactive-streams (org.reactivestreams:reactive-streams:1.0.4 - http://www.reactive-streams.org/)
  • +
  • (The MIT License) semver4j (org.semver4j:semver4j:5.7.1 - https://github.com/semver4j/semver4j)
  • +
  • (MIT) JUL to SLF4J bridge (org.slf4j:jul-to-slf4j:2.0.17 - http://www.slf4j.org)
  • +
  • (MIT) SLF4J API Module (org.slf4j:slf4j-api:2.0.17 - http://www.slf4j.org)
  • +
  • (The Apache Software License, Version 2.0) Aether :: API (org.sonatype.aether:aether-api:1.7 - http://aether.sonatype.org/aether-api/)
  • +
  • (The Apache Software License, Version 2.0) Aether :: Implementation (org.sonatype.aether:aether-impl:1.7 - http://aether.sonatype.org/aether-impl/)
  • +
  • (The Apache Software License, Version 2.0) Aether :: SPI (org.sonatype.aether:aether-spi:1.7 - http://aether.sonatype.org/aether-spi/)
  • +
  • (The Apache Software License, Version 2.0) Aether :: Utilities (org.sonatype.aether:aether-util:1.7 - http://aether.sonatype.org/aether-util/)
  • +
  • (ASL2) org.sonatype.goodies:package-url-java (org.sonatype.goodies:package-url-java:1.2.0 - https://sonatype.github.io/package-url-java/)
  • +
  • (ASL2) org.sonatype.ossindex:ossindex-service-api (org.sonatype.ossindex:ossindex-service-api:1.8.2 - https://sonatype.github.io/ossindex-public/ossindex-service-api/)
  • +
  • (ASL2) org.sonatype.ossindex:ossindex-service-client (org.sonatype.ossindex:ossindex-service-client:1.8.2 - https://sonatype.github.io/ossindex-public/ossindex-service-client/)
  • +
  • (Apache Public License 2.0) Plexus Cipher: encryption/decryption Component (org.sonatype.plexus:plexus-cipher:1.4 - http://spice.sonatype.org/plexus-cipher)
  • +
  • (Apache Public License 2.0) Plexus Security Dispatcher Component (org.sonatype.plexus:plexus-sec-dispatcher:1.3 - http://spice.sonatype.org/plexus-sec-dispatcher)
  • +
  • (The Apache Software License, Version 2.0) Sisu - Guice (org.sonatype.sisu:sisu-guice:2.1.7 - http://forge.sonatype.com/sisu-guice/)
  • +
  • (The Apache Software License, Version 2.0) Sisu - Inject (JSR330 bean support) (org.sonatype.sisu:sisu-inject-bean:1.4.2 - http://sisu.sonatype.org/sisu-inject/guice-bean/sisu-inject-bean/)
  • +
  • (The Apache Software License, Version 2.0) Sisu - Inject (Plexus bean support) (org.sonatype.sisu:sisu-inject-plexus:1.4.2 - http://sisu.sonatype.org/sisu-inject/guice-bean/guice-plexus/sisu-inject-plexus/)
  • +
  • (The Apache License, Version 2.0) springdoc-openapi-starter-common (org.springdoc:springdoc-openapi-starter-common:2.8.9 - https://springdoc.org/springdoc-openapi-starter-common/)
  • +
  • (The Apache License, Version 2.0) springdoc-openapi-starter-webmvc-api (org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.9 - https://springdoc.org/springdoc-openapi-starter-webmvc-api/)
  • +
  • (The Apache License, Version 2.0) springdoc-openapi-starter-webmvc-ui (org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.9 - https://springdoc.org/springdoc-openapi-starter-webmvc-ui/)
  • +
  • (Apache License, Version 2.0) Spring AOP (org.springframework:spring-aop:6.2.8 - https://github.com/spring-projects/spring-framework)
  • +
  • (Apache License, Version 2.0) Spring Beans (org.springframework:spring-beans:6.2.8 - https://github.com/spring-projects/spring-framework)
  • +
  • (Apache License, Version 2.0) Spring Context (org.springframework:spring-context:6.2.8 - https://github.com/spring-projects/spring-framework)
  • +
  • (Apache License, Version 2.0) Spring Core (org.springframework:spring-core:6.2.8 - https://github.com/spring-projects/spring-framework)
  • +
  • (Apache License, Version 2.0) Spring Expression Language (SpEL) (org.springframework:spring-expression:6.2.8 - https://github.com/spring-projects/spring-framework)
  • +
  • (Apache License, Version 2.0) Spring Commons Logging Bridge (org.springframework:spring-jcl:6.2.8 - https://github.com/spring-projects/spring-framework)
  • +
  • (Apache License, Version 2.0) Spring Web (org.springframework:spring-web:6.2.8 - https://github.com/spring-projects/spring-framework)
  • +
  • (Apache License, Version 2.0) Spring Web MVC (org.springframework:spring-webmvc:6.2.8 - https://github.com/spring-projects/spring-framework)
  • +
  • (Apache License, Version 2.0) spring-boot (org.springframework.boot:spring-boot:3.5.3 - https://spring.io/projects/spring-boot)
  • +
  • (Apache License, Version 2.0) spring-boot-actuator (org.springframework.boot:spring-boot-actuator:3.5.3 - https://spring.io/projects/spring-boot)
  • +
  • (Apache License, Version 2.0) spring-boot-actuator-autoconfigure (org.springframework.boot:spring-boot-actuator-autoconfigure:3.5.3 - https://spring.io/projects/spring-boot)
  • +
  • (Apache License, Version 2.0) spring-boot-autoconfigure (org.springframework.boot:spring-boot-autoconfigure:3.5.3 - https://spring.io/projects/spring-boot)
  • +
  • (Apache License, Version 2.0) spring-boot-starter (org.springframework.boot:spring-boot-starter:3.5.3 - https://spring.io/projects/spring-boot)
  • +
  • (Apache License, Version 2.0) spring-boot-starter-actuator (org.springframework.boot:spring-boot-starter-actuator:3.5.3 - https://spring.io/projects/spring-boot)
  • +
  • (Apache License, Version 2.0) spring-boot-starter-json (org.springframework.boot:spring-boot-starter-json:3.5.3 - https://spring.io/projects/spring-boot)
  • +
  • (Apache License, Version 2.0) spring-boot-starter-logging (org.springframework.boot:spring-boot-starter-logging:3.5.3 - https://spring.io/projects/spring-boot)
  • +
  • (Apache License, Version 2.0) spring-boot-starter-thymeleaf (org.springframework.boot:spring-boot-starter-thymeleaf:3.5.3 - https://spring.io/projects/spring-boot)
  • +
  • (Apache License, Version 2.0) spring-boot-starter-tomcat (org.springframework.boot:spring-boot-starter-tomcat:3.5.3 - https://spring.io/projects/spring-boot)
  • +
  • (Apache License, Version 2.0) spring-boot-starter-validation (org.springframework.boot:spring-boot-starter-validation:3.5.3 - https://spring.io/projects/spring-boot)
  • +
  • (Apache License, Version 2.0) spring-boot-starter-web (org.springframework.boot:spring-boot-starter-web:3.5.3 - https://spring.io/projects/spring-boot)
  • +
  • (Apache License, Version 2.0) Spring Cloud Commons (org.springframework.cloud:spring-cloud-commons:4.3.0 - https://projects.spring.io/spring-cloud/spring-cloud-commons/)
  • +
  • (Apache License, Version 2.0) Spring Cloud Context (org.springframework.cloud:spring-cloud-context:4.3.0 - https://projects.spring.io/spring-cloud/spring-cloud-context/)
  • +
  • (Apache License, Version 2.0) spring-cloud-starter (org.springframework.cloud:spring-cloud-starter:4.3.0 - https://projects.spring.io/spring-cloud)
  • +
  • (Apache License, Version 2.0) Spring Cloud Starter Vault Config (org.springframework.cloud:spring-cloud-starter-vault-config:4.3.0 - https://cloud.spring.io/spring-cloud-vault/)
  • +
  • (Apache License, Version 2.0) Spring Cloud Vault Configuration Integration (org.springframework.cloud:spring-cloud-vault-config:4.3.0 - https://spring.io/spring-cloud/spring-cloud-vault-parent/spring-cloud-vault-config)
  • +
  • (Apache License, Version 2.0) spring-security-config (org.springframework.security:spring-security-config:6.5.1 - https://spring.io/projects/spring-security)
  • +
  • (Apache License, Version 2.0) spring-security-core (org.springframework.security:spring-security-core:6.5.1 - https://spring.io/projects/spring-security)
  • +
  • (Apache License, Version 2.0) spring-security-crypto (org.springframework.security:spring-security-crypto:6.5.1 - https://spring.io/projects/spring-security)
  • +
  • (Apache License, Version 2.0) spring-security-web (org.springframework.security:spring-security-web:6.5.1 - https://spring.io/projects/spring-security)
  • +
  • (Apache License, Version 2.0) Spring Vault Core (org.springframework.vault:spring-vault-core:3.2.0 - https://projects.spring.io/spring-vault/spring-vault-core/)
  • +
  • (MIT) Testcontainers :: JUnit Jupiter Extension (org.testcontainers:junit-jupiter:1.21.2 - https://java.testcontainers.org)
  • +
  • (BSD-3-Clause) ThreeTen backport (org.threeten:threetenbp:1.7.0 - https://www.threeten.org/threetenbp)
  • +
  • (The Apache Software License, Version 2.0) thymeleaf (org.thymeleaf:thymeleaf:3.1.3.RELEASE - http://www.thymeleaf.org/thymeleaf-lib/thymeleaf)
  • +
  • (The Apache Software License, Version 2.0) thymeleaf-spring6 (org.thymeleaf:thymeleaf-spring6:3.1.3.RELEASE - http://www.thymeleaf.org/thymeleaf-lib/thymeleaf-spring6)
  • +
  • (The Apache Software License, Version 2.0) thymeleaf-extras-springsecurity6 (org.thymeleaf.extras:thymeleaf-extras-springsecurity6:3.1.3.RELEASE - http://www.thymeleaf.org/thymeleaf-lib/thymeleaf-extras-springsecurity6)
  • +
  • (Public Domain) XZ for Java (org.tukaani:xz:1.9 - https://tukaani.org/xz/java.html)
  • +
  • (The Apache Software License, Version 2.0) unbescape (org.unbescape:unbescape:1.1.6.RELEASE - http://www.unbescape.org)
  • +
  • (Apache License, Version 2.0) Bootstrap (org.webjars:bootstrap:5.3.7 - http://webjars.org)
  • +
  • (MIT) DataTables (org.webjars:datatables:2.3.0 - http://webjars.org)
  • +
  • (MIT License) jquery (org.webjars:jquery:3.7.1 - http://webjars.org)
  • +
  • (Apache-2.0) Swagger UI (org.webjars:swagger-ui:5.21.0 - https://www.webjars.org)
  • +
  • (MIT) webjars-locator-lite (org.webjars:webjars-locator-lite:1.1.0 - https://webjars.org)
  • +
  • (BSD 2-Clause) github-buttons (org.webjars.npm:github-buttons:2.14.1 - https://www.webjars.org)
  • +
  • (Common Public 1.0) pecoff4j (org.whitesource:pecoff4j:0.0.2.1 - https://github.com/whitesource/pecoff4j-maven)
  • +
  • (Apache License, Version 2.0) SnakeYAML (org.yaml:snakeyaml:2.4 - https://bitbucket.org/snakeyaml/snakeyaml)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: Annotations (software.amazon.awssdk:annotations:2.31.77 - https://aws.amazon.com/sdkforjava/core/annotations)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: HTTP Clients :: Apache (software.amazon.awssdk:apache-client:2.31.77 - https://aws.amazon.com/sdkforjava/http-clients/apache-client)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: Auth (software.amazon.awssdk:auth:2.31.77 - https://aws.amazon.com/sdkforjava)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: AWS Core (software.amazon.awssdk:aws-core:2.31.77 - https://aws.amazon.com/sdkforjava)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: Core :: Protocols :: AWS Json Protocol (software.amazon.awssdk:aws-json-protocol:2.31.77 - https://aws.amazon.com/sdkforjava)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: Core :: Protocols :: AWS Query Protocol (software.amazon.awssdk:aws-query-protocol:2.31.77 - https://aws.amazon.com/sdkforjava)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: Checksums (software.amazon.awssdk:checksums:2.31.77 - https://aws.amazon.com/sdkforjava)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: Checksums SPI (software.amazon.awssdk:checksums-spi:2.31.77 - https://aws.amazon.com/sdkforjava)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: Endpoints SPI (software.amazon.awssdk:endpoints-spi:2.31.77 - https://aws.amazon.com/sdkforjava/core/endpoints-spi)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: HTTP Auth (software.amazon.awssdk:http-auth:2.31.77 - https://aws.amazon.com/sdkforjava)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: HTTP Auth AWS (software.amazon.awssdk:http-auth-aws:2.31.77 - https://aws.amazon.com/sdkforjava)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: HTTP Auth Event Stream (software.amazon.awssdk:http-auth-aws-eventstream:2.31.77 - https://aws.amazon.com/sdkforjava)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: HTTP Auth SPI (software.amazon.awssdk:http-auth-spi:2.31.77 - https://aws.amazon.com/sdkforjava)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: HTTP Client Interface (software.amazon.awssdk:http-client-spi:2.31.77 - https://aws.amazon.com/sdkforjava/http-client-spi)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: Identity SPI (software.amazon.awssdk:identity-spi:2.31.77 - https://aws.amazon.com/sdkforjava)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: Core :: Protocols :: Json Utils (software.amazon.awssdk:json-utils:2.31.77 - https://aws.amazon.com/sdkforjava)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: Metrics SPI (software.amazon.awssdk:metrics-spi:2.31.77 - https://aws.amazon.com/sdkforjava/core/metrics-spi)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: HTTP Clients :: Netty Non-Blocking I/O (software.amazon.awssdk:netty-nio-client:2.31.77 - https://aws.amazon.com/sdkforjava/http-clients/netty-nio-client)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: Profiles (software.amazon.awssdk:profiles:2.31.77 - https://aws.amazon.com/sdkforjava)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: Core :: Protocols :: Protocol Core (software.amazon.awssdk:protocol-core:2.31.77 - https://aws.amazon.com/sdkforjava)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: Regions (software.amazon.awssdk:regions:2.31.77 - https://aws.amazon.com/sdkforjava/core/regions)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: Retries (software.amazon.awssdk:retries:2.31.77 - https://aws.amazon.com/sdkforjava/core/retries)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: Retries API (software.amazon.awssdk:retries-spi:2.31.77 - https://aws.amazon.com/sdkforjava/core/retries-spi)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: SDK Core (software.amazon.awssdk:sdk-core:2.31.77 - https://aws.amazon.com/sdkforjava)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: Services :: AWS Simple Systems Management (SSM) (software.amazon.awssdk:ssm:2.31.77 - https://aws.amazon.com/sdkforjava)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: Services :: AWS STS (software.amazon.awssdk:sts:2.31.77 - https://aws.amazon.com/sdkforjava)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: Third Party :: Jackson-core (software.amazon.awssdk:third-party-jackson-core:2.31.77 - https://aws.amazon.com/sdkforjava)
  • +
  • (Apache License, Version 2.0) AWS Java SDK :: Utilities (software.amazon.awssdk:utils:2.31.77 - https://aws.amazon.com/sdkforjava/utils)
  • +
  • (Apache License, Version 2.0) AWS Event Stream (software.amazon.eventstream:eventstream:1.0.1 - https://github.com/awslabs/aws-eventstream-java)
  • +
  • (Apache-2.0) CPE Parser (us.springett:cpe-parser:3.0.0 - https://github.com/stevespringett/CPE-Parser)
  • + +
+
+
+
+
+
+ + diff --git a/static-site/pr-123/pages/challenge-57.html b/static-site/pr-123/pages/challenge-57.html new file mode 100644 index 000000000..06dc2bc5e --- /dev/null +++ b/static-site/pr-123/pages/challenge-57.html @@ -0,0 +1,124 @@ + + + + + + Challenge 57: LLM API Key Exposure - Preview + + + + + +
+
+

📋 Challenge 57 Preview

+

This is a static preview of Challenge 57: LLM API Key Exposure in Client-Side JavaScript

+
+ +
+
+

🤖 Challenge 57: LLM API Key Exposure

+
+ AI + ⭐⭐ +
+
+ +
+
+

📋 Challenge Description

+

This challenge demonstrates a critical security vulnerability in modern AI-powered web applications: LLM API keys exposed in client-side JavaScript code.

+ +

As developers rush to integrate AI capabilities, many make the critical mistake of putting sensitive API credentials directly in browser-accessible code.

+ +
+ ⚠️ Vulnerability: API keys exposed in client-side JavaScript can lead to massive financial losses, service disruption, and data harvesting. +
+ +
🎯 Your Mission
+

Find the exposed LLM API key in the client-side JavaScript code. Look for:

+
    +
  • API keys starting with "sk-"
  • +
  • JavaScript variables storing credentials
  • +
  • Console.log statements with sensitive data
  • +
  • Authorization headers in network requests
  • +
+
+ +
+

💻 Code Preview

+
+// AI Chat Application - Client-side JavaScript +class LLMChatApp { + constructor() { + // WARNING: This is a security anti-pattern! + // API keys should NEVER be exposed in client-side code + this.apiKey = 'sk-llm-api-key-abc123def456ghi789jkl012mno345pqr678stu901vwx234yzA'; + this.apiEndpoint = 'https://api.example-llm.com/v1/chat/completions'; + this.initializeChat(); + } + + async sendMessage() { + try { + const response = await fetch(this.apiEndpoint, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${this.apiKey}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + model: 'gpt-3.5-turbo', + messages: [{role: 'user', content: message}] + }) + }); + } catch (error) { + console.log('Failed request used API key:', this.apiKey); + } + } +} + +// Debug: Log the API key for development (another anti-pattern!) +console.log('Debug: LLM API Key = sk-llm-api-key-abc123def456ghi789jkl012mno345pqr678stu901vwx234yzA'); +
+ +
+
🔍 How to Explore:
+
    +
  1. Visit /llm-demo to see the vulnerable chat application
  2. +
  3. Open Developer Tools (F12)
  4. +
  5. Check the /llm-chat.js file
  6. +
  7. Monitor console for debug messages
  8. +
  9. Examine network requests
  10. +
+
+
+
+ +
+

🏆 Learning Objectives

+
+
+ 💰 Financial Impact +

LLM API calls can be extremely expensive. Exposed keys have led to bills of tens of thousands of dollars within hours.

+
+
+ 🔒 Security Risks +

Attackers can use your API keys for data harvesting, service disruption, and generating harmful content.

+
+
+ 🛡️ Prevention +

Always use server-side proxies, environment variables, and proper access controls for AI service credentials.

+
+
+
+
+
+ + \ No newline at end of file diff --git a/static-site/pr-123/pages/challenge-58.html b/static-site/pr-123/pages/challenge-58.html new file mode 100644 index 000000000..8d808d6f3 --- /dev/null +++ b/static-site/pr-123/pages/challenge-58.html @@ -0,0 +1,131 @@ + + + + + + Challenge 58: Database Connection String Exposure - Preview + + + + + +
+
+

📋 Challenge 58 Preview

+

This is a static preview of Challenge 58: Database Connection String Exposure through Error Messages

+
+ +
+
+

🗄️ Challenge 58: Database Connection String Exposure

+
+ LOGGING + ⭐⭐ +
+
+ +
+
+

📋 Challenge Description

+

This challenge demonstrates one of the most common and dangerous ways secrets leak in real-world applications: database connection strings with embedded credentials exposed through error messages.

+ +

When applications fail to connect to databases, they often expose the full connection string (including usernames and passwords) in error messages, logs, or even user-facing interfaces.

+ +
+ ⚠️ Critical Risk: Database connection string exposure can lead to direct database access and complete data breaches. +
+ +
🎯 Your Mission
+

Trigger a database connection error and find the exposed password. Look for:

+
    +
  • JDBC connection URLs with embedded credentials
  • +
  • Error messages containing connection strings
  • +
  • Log entries with sensitive information
  • +
  • Patterns like password=SECRET
  • +
+
+ +
+

💻 Vulnerable Code Example

+
+// Simulated database connection string with embedded credentials +private static final String DB_CONNECTION_STRING = + "jdbc:postgresql://db.example.com:5432/userdb?" + + "user=dbadmin&password=SuperSecretDB2024!&ssl=true"; + +public String simulateDatabaseConnectionError() { + try { + // This will fail and expose the connection string + Connection conn = DriverManager.getConnection(DB_CONNECTION_STRING); + return "Connection successful"; + } catch (SQLException e) { + // Poor error handling - exposing full connection string + String errorMessage = + "Database connection failed with connection string: " + + DB_CONNECTION_STRING + " +Error: " + e.getMessage(); + + // Credentials also get logged (another exposure vector) + log.error("Failed to connect to database: {}", errorMessage); + + return errorMessage; + } +} +
+ +
+ Example Error Output:
+ Database connection failed with connection string: jdbc:postgresql://db.example.com:5432/userdb?user=dbadmin&password=SuperSecretDB2024!&ssl=true +
+
+
+ +
+

🔍 How to Trigger the Error

+

Visit the /error-demo/database-connection endpoint to simulate a database connection failure that exposes credentials in both the HTTP response and application logs.

+
+ +
+

🏆 Learning Objectives

+
+
+ 🚨 Common Exposure Vectors +
    +
  • Application startup logs
  • +
  • Health check failures
  • +
  • CI/CD pipeline logs
  • +
  • Error tracking services
  • +
+
+
+ 💥 Real-World Impact +
    +
  • Production database compromises
  • +
  • Complete data breaches
  • +
  • Lateral movement attacks
  • +
  • Compliance violations
  • +
+
+
+ 🛡️ Prevention +
    +
  • External secret management
  • +
  • Error message sanitization
  • +
  • Separate database credentials
  • +
  • Connection pooling
  • +
+
+
+
+
+
+ + \ No newline at end of file diff --git a/static-site/pr-123/pages/challenge-example.html b/static-site/pr-123/pages/challenge-example.html new file mode 100644 index 000000000..7272568d4 --- /dev/null +++ b/static-site/pr-123/pages/challenge-example.html @@ -0,0 +1,128 @@ + + + +
+
+
📋 Static Preview Notice
+ This is a static preview of PR #123. Some dynamic content may be simplified or use mock data. +
+ +

Challenge 1: Find the hard-coded password< /> ⭐<> +

+ +

+ + +
+
+
+
+ + +
+ + +
+
+ + + 💡 Tip: Secrets are often strings, numbers, or encoded values. Copy and paste exactly what you find. +
+
+ + +
+
+ + +
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ + +
+ +
+ + diff --git a/static-site/pr-123/pages/stats.html b/static-site/pr-123/pages/stats.html new file mode 100644 index 000000000..7c8f4ce6d --- /dev/null +++ b/static-site/pr-123/pages/stats.html @@ -0,0 +1,73 @@ + + + + +
+
+
📋 Static Preview Notice
+ This is a static preview of PR #123. Some dynamic content may be simplified or use mock data. +
+

Current Stats & Config

+

+ +
+
+
+

Stats

+

+ Number of active sessions: 15
+ Number of canary callbacks since boot: 3
+

+
+

+ +
+
+
+
+

Config

+

+ Hints enabled: True
+ Reason enabled: True
+ CTF-mode enabled: False
+ Spoilers enabled: True
+ Springdoc enabled: True + Swagger UI enabled: True +
Swagger uri: +
+

+
+
+
+
+
+

Canary callbacks

+

Number of canary callbacks since boot: 3

+

Last canary token received:

+

+

CanaryTokenUrlwebtoken in use:

+
  • https://example.canarytokens.org/token1
  • +
  • https://example.canarytokens.org/token2
  • +
+

Note that, when accessing the URLs of the CanaryTokens, some of your data is being logged by the + Canarytoken service.

+
+
+
+ + +
+ + + diff --git a/static-site/pr-123/pages/welcome.html b/static-site/pr-123/pages/welcome.html new file mode 100644 index 000000000..4c3749efa --- /dev/null +++ b/static-site/pr-123/pages/welcome.html @@ -0,0 +1,1144 @@ + + + + +
+
+
📋 Static Preview Notice
+ This is a static preview of PR #123. Some dynamic content may be simplified or use mock data. +
+
+
Welcome to OWASP WrongSecrets
+

+ Learn about secrets management by finding real secrets hidden in code, configuration files, and cloud infrastructure. +

+
+ + + +

+ Pro Tip: Each challenge below has a different difficulty level and may require different environments. + Start with the easier ones and work your way up! 🚀 +

+
+
+
+
+
+ + Difficulty: ⭐ (Easy) ⭐⭐ (Medium) ⭐⭐⭐ (Hard) ⭐⭐⭐⭐ (Expert) ⭐⭐⭐⭐⭐ (Master) | + Environment: Where the challenge can be solved + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
# Challenge     Focus    + Difficulty        + + Solved
0 +   + + Find the hard-coded password + + DEVOPSDocker + +
1 +   + + Find the unencrypted password in Git + + GIT⭐⭐Docker + +
2 +   + + Find the hard-coded password in front-end + + FRONTEND⭐⭐⭐Docker + +
3 +   + + Take a look at this file + + DEVOPS⭐⭐⭐⭐Docker + +
4 +   + + Find the AWS S3 bucket password + + AWS⭐⭐⭐⭐⭐AWS + +
5 +   + + Find the Azure Key Vault secret + + AZUREAzure + +
6 +   + + Connect the dots with Docker + + DOCKER⭐⭐Docker + +
7 +   + + Find the secret in the container + + DOCKER⭐⭐⭐Docker + +
8 +   + + Retrieve cloud instance metadata + + AWS⭐⭐⭐⭐AWS + +
9 +   + + Use AWS Parameter Store + + AWS⭐⭐⭐⭐⭐AWS + +
10 +   + + Find the secret in SSM + + AWSAWS + +
11 +   + + Find the Docker secret + + DOCKER⭐⭐Docker + +
12 +   + + GitHub Actions secret + + CI/CD⭐⭐⭐Docker + +
13 +   + + Find the K8s secret + + K8S⭐⭐⭐⭐K8s + +
14 +   + + Find the hardcoded secret + + DEVOPS⭐⭐⭐⭐⭐Docker + +
15 +   + + Front-end secret exposure + + FRONTENDDocker + +
16 +   + + Bash history secret + + DEVOPS⭐⭐Docker + +
17 +   + + Find the secret in logs + + LOGGING⭐⭐⭐Docker + +
18 +   + + Find the encrypted secret + + CRYPTO⭐⭐⭐⭐Docker + +
19 +   + + Binary analysis secret + + BINARY⭐⭐⭐⭐⭐Docker + +
20 +   + + Go binary secret + + BINARYDocker + +
21 +   + + Rust binary secret + + BINARY⭐⭐Docker + +
22 +   + + Front-end secret part 2 + + FRONTEND⭐⭐⭐Docker + +
23 +   + + Web3 secret exposure + + WEB3⭐⭐⭐⭐Docker + +
24 +   + + Smart contract secret + + WEB3⭐⭐⭐⭐⭐Docker + +
25 +   + + Binary secret 2 + + BINARYDocker + +
26 +   + + Terraform secret + + TERRAFORM⭐⭐AWS + +
27 +   + + GitHub issue secret + + GIT⭐⭐⭐Docker + +
28 +   + + Log analysis secret + + LOGGING⭐⭐⭐⭐Docker + +
29 +   + + Web page secret + + FRONTEND⭐⭐⭐⭐⭐Docker + +
30 +   + + AI prompt injection + + AIDocker + +
31 +   + + Container debugging + + DOCKER⭐⭐K8s + +
32 +   + + Password shucking + + PASSWORD⭐⭐⭐Docker + +
33 +   + + Random key generation + + CRYPTO⭐⭐⭐⭐Docker + +
34 +   + + Vulnerability reporting + + DOCS⭐⭐⭐⭐⭐Docker + +
35 +   + + Binary without strings + + BINARYDocker + +
36 +   + + Security test access + + CI/CD⭐⭐Docker + +
37 +   + + Git notes secret + + GIT⭐⭐⭐Docker + +
38 +   + + Insecure encryption key + + CRYPTO⭐⭐⭐⭐Docker + +
39 +   + + Encryption key storage + + CRYPTO⭐⭐⭐⭐⭐Docker + +
40 +   + + Password shucking 2 + + PASSWORDDocker + +
41 +   + + Audit event secret + + LOGGING⭐⭐Docker + +
42 +   + + Vault template injection + + VAULT⭐⭐⭐K8s with Vault + +
43 +   + + Multi-environment secret + + VAULT⭐⭐⭐⭐K8s with Vault + +
44 +   + + Vault subkey challenge + + VAULT⭐⭐⭐⭐⭐K8s with Vault + +
45 +   + + HashiCorp Vault injection + + VAULTK8s with Vault + +
46 +   + + Binary secret 3 + + BINARY⭐⭐Docker + +
47 +   + + Binary secret 4 + + BINARY⭐⭐⭐Docker + +
48 +   + + AES MD5 cracking + + CRYPTO⭐⭐⭐⭐Docker + +
49 +   + + Docker secret 2 + + DOCKER⭐⭐⭐⭐⭐Docker + +
50 +   + + Binary secret 5 + + BINARYDocker + +
51 +   + + Docker secret 3 + + DOCKER⭐⭐Docker + +
52 +   + + Container debugging 2 + + DOCKER⭐⭐⭐K8s + +
53 +   + + Docker secret 4 + + DOCKER⭐⭐⭐⭐Docker + +
54 +   + + SSH bastion secret + + DOCKER⭐⭐⭐⭐⭐Docker + +
55 +   + + AI secret exposure + + AIDocker + +
56 +   + + LLM API key exposure in JavaScript + + AI⭐⭐Docker + +
57 +   + + Database connection string exposure + + LOGGING⭐⭐⭐Docker + +
+

Total score: 42

+

+ +

+ + +

Hasty? Here is the Vault secret;-) +

+
+
+
+ Like what you see? Please
+ Star us on Github +
Note: The above button only takes you to the repository. Please ensure + to + star the repository once you are there! +
+
+
+ + + + + + OWASP WrongSecrets - Preview + + + + + + + + + OWASP Project Leaders: + + Top Contributors: + + Contributors: + + Testers: + + Special mentions for helping out: + +
+
+
+ +
+ Wondering what a secret is? A secret is often a confidential piece of information that is + required to unlock certain functionalities or information. It can exists in many shapes or + forms, for instance: +
    +
  • 2FA keys
  • +
  • Activation/Callback links
  • +
  • API keys
  • +
  • Credentials
  • +
  • Passwords
  • +
  • Private keys (decryption, signing, TLS, SSH, GPG)
  • +
  • Secret keys (symmetric encryption, HMAC)
  • +
  • Session cookies
  • +
  • Tokens (Session, Refresh, Authentication, Activation, etc.)
  • +
+
+
+ Want to see if your tool of choice detects all the secrets available in this project? +
+ + Check the instructions in the README + . +
+
+ Developing our solution in 3 clouds costs money. Want to help us to cover our cloud bills? + Donate. +
+
+
+
+
+ + + From e2449d377def0b41b3bffed8d77f988fbd144612 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 3 Aug 2025 01:12:58 +0000 Subject: [PATCH 06/17] Fix spotless formatting violations and Python script syntax warning Co-authored-by: commjoen <1457214+commjoen@users.noreply.github.com> --- .github/scripts/remove_pr_from_index.py | 2 +- .../challenges/docker/Challenge57.java | 182 +++++++++--------- .../docker/Challenge57Controller.java | 86 ++++----- .../challenges/docker/Challenge58.java | 2 +- .../docker/Challenge58Controller.java | 2 +- .../challenges/docker/Challenge57Test.java | 17 +- .../docker/Challenge58ControllerTest.java | 2 +- .../challenges/docker/Challenge58Test.java | 2 +- 8 files changed, 152 insertions(+), 143 deletions(-) diff --git a/.github/scripts/remove_pr_from_index.py b/.github/scripts/remove_pr_from_index.py index a7a9d50ef..84c325ec1 100644 --- a/.github/scripts/remove_pr_from_index.py +++ b/.github/scripts/remove_pr_from_index.py @@ -14,7 +14,7 @@ def main(): # Remove the PR card for this specific PR number card_pattern = ( - f'
]*data-pr="{pr_number}"[^>]*>.*?
\s*' + rf'
]*data-pr="{pr_number}"[^>]*>.*?
\s*' ) updated_content = re.sub(card_pattern, "", content, flags=re.DOTALL) diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57.java index 24e2341e7..ec5d86a20 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57.java @@ -12,7 +12,8 @@ public class Challenge57 implements Challenge { // Simulated LLM API key that would be exposed in client-side JavaScript - private static final String LLM_API_KEY = "sk-llm-api-key-abc123def456ghi789jkl012mno345pqr678stu901vwx234yzA"; + private static final String LLM_API_KEY = + "sk-llm-api-key-abc123def456ghi789jkl012mno345pqr678stu901vwx234yzA"; @Override public Spoiler spoiler() { @@ -25,97 +26,98 @@ public boolean answerCorrect(String answer) { } /** - * This method returns JavaScript code that would typically be served to the browser, - * containing an exposed LLM API key. This demonstrates how sensitive API keys can be - * accidentally exposed in client-side code. + * This method returns JavaScript code that would typically be served to the browser, containing + * an exposed LLM API key. This demonstrates how sensitive API keys can be accidentally exposed in + * client-side code. */ public String getLLMJavaScriptCode() { return """ - // AI Chat Application - Client-side JavaScript - class LLMChatApp { - constructor() { - // WARNING: This is a security anti-pattern! - // API keys should NEVER be exposed in client-side code - this.apiKey = '%s'; - this.apiEndpoint = 'https://api.example-llm.com/v1/chat/completions'; - this.initializeChat(); - } - - async initializeChat() { - console.log('Initializing LLM chat with API key:', this.apiKey); - this.setupEventListeners(); - } - - setupEventListeners() { - document.getElementById('send-button').addEventListener('click', () => { - this.sendMessage(); - }); - - document.getElementById('message-input').addEventListener('keypress', (e) => { - if (e.key === 'Enter') { - this.sendMessage(); - } - }); - } - - async sendMessage() { - const messageInput = document.getElementById('message-input'); - const message = messageInput.value.trim(); - - if (!message) return; - - try { - const response = await fetch(this.apiEndpoint, { - method: 'POST', - headers: { - 'Authorization': `Bearer ${this.apiKey}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - model: 'gpt-3.5-turbo', - messages: [ - {role: 'user', content: message} - ], - max_tokens: 150 - }) - }); - - const data = await response.json(); - this.displayResponse(data.choices[0].message.content); - - } catch (error) { - console.error('LLM API Error:', error); - console.log('Failed request used API key:', this.apiKey); - this.displayError('Failed to get response from LLM service'); - } - - messageInput.value = ''; - } - - displayResponse(text) { - const chatOutput = document.getElementById('chat-output'); - const responseDiv = document.createElement('div'); - responseDiv.className = 'llm-response'; - responseDiv.textContent = text; - chatOutput.appendChild(responseDiv); - chatOutput.scrollTop = chatOutput.scrollHeight; - } - - displayError(error) { - const chatOutput = document.getElementById('chat-output'); - const errorDiv = document.createElement('div'); - errorDiv.className = 'error-message'; - errorDiv.textContent = error; - chatOutput.appendChild(errorDiv); - } - } - - // Initialize the chat app when page loads - document.addEventListener('DOMContentLoaded', () => { - // Debug: Log the API key for development (another anti-pattern!) - console.log('Debug: LLM API Key = %s'); - new LLMChatApp(); - }); - """.formatted(LLM_API_KEY, LLM_API_KEY); + // AI Chat Application - Client-side JavaScript + class LLMChatApp { + constructor() { + // WARNING: This is a security anti-pattern! + // API keys should NEVER be exposed in client-side code + this.apiKey = '%s'; + this.apiEndpoint = 'https://api.example-llm.com/v1/chat/completions'; + this.initializeChat(); + } + + async initializeChat() { + console.log('Initializing LLM chat with API key:', this.apiKey); + this.setupEventListeners(); + } + + setupEventListeners() { + document.getElementById('send-button').addEventListener('click', () => { + this.sendMessage(); + }); + + document.getElementById('message-input').addEventListener('keypress', (e) => { + if (e.key === 'Enter') { + this.sendMessage(); + } + }); + } + + async sendMessage() { + const messageInput = document.getElementById('message-input'); + const message = messageInput.value.trim(); + + if (!message) return; + + try { + const response = await fetch(this.apiEndpoint, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${this.apiKey}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + model: 'gpt-3.5-turbo', + messages: [ + {role: 'user', content: message} + ], + max_tokens: 150 + }) + }); + + const data = await response.json(); + this.displayResponse(data.choices[0].message.content); + + } catch (error) { + console.error('LLM API Error:', error); + console.log('Failed request used API key:', this.apiKey); + this.displayError('Failed to get response from LLM service'); + } + + messageInput.value = ''; + } + + displayResponse(text) { + const chatOutput = document.getElementById('chat-output'); + const responseDiv = document.createElement('div'); + responseDiv.className = 'llm-response'; + responseDiv.textContent = text; + chatOutput.appendChild(responseDiv); + chatOutput.scrollTop = chatOutput.scrollHeight; + } + + displayError(error) { + const chatOutput = document.getElementById('chat-output'); + const errorDiv = document.createElement('div'); + errorDiv.className = 'error-message'; + errorDiv.textContent = error; + chatOutput.appendChild(errorDiv); + } + } + + // Initialize the chat app when page loads + document.addEventListener('DOMContentLoaded', () => { + // Debug: Log the API key for development (another anti-pattern!) + console.log('Debug: LLM API Key = %s'); + new LLMChatApp(); + }); + """ + .formatted(LLM_API_KEY, LLM_API_KEY); } } diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Controller.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Controller.java index 31a8525f2..57cee2dc2 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Controller.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Controller.java @@ -15,58 +15,56 @@ public class Challenge57Controller { private final Challenge57 challenge; /** - * Endpoint to serve JavaScript code that contains an exposed LLM API key. - * This simulates how developers accidentally expose API keys in client-side code. + * Endpoint to serve JavaScript code that contains an exposed LLM API key. This simulates how + * developers accidentally expose API keys in client-side code. */ @GetMapping(value = "/llm-chat.js", produces = MediaType.APPLICATION_JAVASCRIPT_VALUE) public String getLLMJavaScript() { log.info("Serving LLM JavaScript for Challenge 57..."); return challenge.getLLMJavaScriptCode(); } - - /** - * Endpoint to serve a simple HTML page that loads the vulnerable JavaScript. - */ + + /** Endpoint to serve a simple HTML page that loads the vulnerable JavaScript. */ @GetMapping(value = "/llm-demo", produces = MediaType.TEXT_HTML_VALUE) public String getLLMDemoPage() { return """ - - - - - - LLM Chat Demo - Challenge 57 - - - -

🤖 LLM Chat Demo

-
- ⚠️ Security Notice: This demo application contains a common security vulnerability. - Can you find the exposed API key? -
- -
-
- - -
- -
-

Hint: Check the browser's developer tools (F12) and look at the Network tab or Console.

-

You can also view the source code of the JavaScript file directly.

-
- - - - - """; + + + + + + LLM Chat Demo - Challenge 57 + + + +

🤖 LLM Chat Demo

+
+ ⚠️ Security Notice: This demo application contains a common security vulnerability. + Can you find the exposed API key? +
+ +
+
+ + +
+ +
+

Hint: Check the browser's developer tools (F12) and look at the Network tab or Console.

+

You can also view the source code of the JavaScript file directly.

+
+ + + + +"""; } } diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge58.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge58.java index 7fd538f16..849232935 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge58.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge58.java @@ -57,4 +57,4 @@ public String simulateDatabaseConnectionError() { return errorMessage; } } -} \ No newline at end of file +} diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge58Controller.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge58Controller.java index 609db6eb8..04b457830 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge58Controller.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge58Controller.java @@ -23,4 +23,4 @@ public String triggerDatabaseError() { log.info("Attempting database connection for Challenge 58..."); return challenge.simulateDatabaseConnectionError(); } -} \ No newline at end of file +} diff --git a/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Test.java b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Test.java index a09d775b1..3496429a4 100644 --- a/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Test.java +++ b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Test.java @@ -10,13 +10,18 @@ class Challenge57Test { @Test void spoilerShouldReturnCorrectAnswer() { var challenge = new Challenge57(); - assertThat(challenge.spoiler()).isEqualTo(new Spoiler("sk-llm-api-key-abc123def456ghi789jkl012mno345pqr678stu901vwx234yzA")); + assertThat(challenge.spoiler()) + .isEqualTo( + new Spoiler("sk-llm-api-key-abc123def456ghi789jkl012mno345pqr678stu901vwx234yzA")); } @Test void answerCorrectShouldReturnTrueForCorrectAnswer() { var challenge = new Challenge57(); - assertThat(challenge.answerCorrect("sk-llm-api-key-abc123def456ghi789jkl012mno345pqr678stu901vwx234yzA")).isTrue(); + assertThat( + challenge.answerCorrect( + "sk-llm-api-key-abc123def456ghi789jkl012mno345pqr678stu901vwx234yzA")) + .isTrue(); } @Test @@ -30,7 +35,10 @@ void answerCorrectShouldReturnFalseForIncorrectAnswer() { @Test void answerCorrectShouldTrimWhitespace() { var challenge = new Challenge57(); - assertThat(challenge.answerCorrect(" sk-llm-api-key-abc123def456ghi789jkl012mno345pqr678stu901vwx234yzA ")).isTrue(); + assertThat( + challenge.answerCorrect( + " sk-llm-api-key-abc123def456ghi789jkl012mno345pqr678stu901vwx234yzA ")) + .isTrue(); } @Test @@ -39,7 +47,8 @@ void getLLMJavaScriptCodeShouldExposeAPIKey() { String jsCode = challenge.getLLMJavaScriptCode(); // Verify that the JavaScript code contains the exposed API key - assertThat(jsCode).contains("sk-llm-api-key-abc123def456ghi789jkl012mno345pqr678stu901vwx234yzA"); + assertThat(jsCode) + .contains("sk-llm-api-key-abc123def456ghi789jkl012mno345pqr678stu901vwx234yzA"); assertThat(jsCode).contains("this.apiKey = "); assertThat(jsCode).contains("console.log"); assertThat(jsCode).contains("Authorization"); diff --git a/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge58ControllerTest.java b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge58ControllerTest.java index f4674f596..6c2b4c828 100644 --- a/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge58ControllerTest.java +++ b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge58ControllerTest.java @@ -28,4 +28,4 @@ void triggerDatabaseErrorShouldReturnErrorMessage() { assertThat(result).isEqualTo(expectedError); org.mockito.Mockito.verify(challenge).simulateDatabaseConnectionError(); } -} \ No newline at end of file +} diff --git a/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge58Test.java b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge58Test.java index cc044ab49..adc6a1639 100644 --- a/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge58Test.java +++ b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge58Test.java @@ -44,4 +44,4 @@ void simulateDatabaseConnectionErrorShouldExposeConnectionString() { assertThat(errorMessage).contains("password=SuperSecretDB2024!"); assertThat(errorMessage).contains("Database connection failed"); } -} \ No newline at end of file +} From 9cf72464f5f1ddef57a5b1424363603b31738441 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 3 Aug 2025 01:16:32 +0000 Subject: [PATCH 07/17] Fix MediaType constant issue in Challenge57Controller Co-authored-by: commjoen <1457214+commjoen@users.noreply.github.com> --- .../wrongsecrets/challenges/docker/Challenge57Controller.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Controller.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Controller.java index 57cee2dc2..c51b06b06 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Controller.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Controller.java @@ -18,7 +18,7 @@ public class Challenge57Controller { * Endpoint to serve JavaScript code that contains an exposed LLM API key. This simulates how * developers accidentally expose API keys in client-side code. */ - @GetMapping(value = "/llm-chat.js", produces = MediaType.APPLICATION_JAVASCRIPT_VALUE) + @GetMapping(value = "/llm-chat.js", produces = "application/javascript") public String getLLMJavaScript() { log.info("Serving LLM JavaScript for Challenge 57..."); return challenge.getLLMJavaScriptCode(); From cc8b7217ca54f726d274a3cc03b64c6d0ab0a397 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 3 Aug 2025 01:31:44 +0000 Subject: [PATCH 08/17] Fix workflow failures: Black formatting and SpotBugs violation Co-authored-by: commjoen <1457214+commjoen@users.noreply.github.com> --- .../scripts/generate_thymeleaf_previews.py | 128 ++++++++++++++++-- .../challenges/docker/Challenge57.java | 2 + 2 files changed, 118 insertions(+), 12 deletions(-) diff --git a/.github/scripts/generate_thymeleaf_previews.py b/.github/scripts/generate_thymeleaf_previews.py index 4e3a301a2..746cda931 100755 --- a/.github/scripts/generate_thymeleaf_previews.py +++ b/.github/scripts/generate_thymeleaf_previews.py @@ -107,20 +107,124 @@ def generate_mock_challenges(self): difficulties = ["⭐", "⭐⭐", "⭐⭐⭐", "⭐⭐⭐⭐", "⭐⭐⭐⭐⭐"] techs = [ - "DEVOPS", "GIT", "FRONTEND", "DEVOPS", "AWS", "AZURE", "DOCKER", "DOCKER", "AWS", "AWS", - "AWS", "DOCKER", "CI/CD", "K8S", "DEVOPS", "FRONTEND", "DEVOPS", "LOGGING", "CRYPTO", "BINARY", - "BINARY", "BINARY", "FRONTEND", "WEB3", "WEB3", "BINARY", "TERRAFORM", "GIT", "LOGGING", "FRONTEND", - "AI", "DOCKER", "PASSWORD", "CRYPTO", "DOCS", "BINARY", "CI/CD", "GIT", "CRYPTO", "CRYPTO", - "PASSWORD", "LOGGING", "VAULT", "VAULT", "VAULT", "VAULT", "BINARY", "BINARY", "CRYPTO", "DOCKER", - "BINARY", "DOCKER", "DOCKER", "DOCKER", "DOCKER", "AI", "AI", "LOGGING" + "DEVOPS", + "GIT", + "FRONTEND", + "DEVOPS", + "AWS", + "AZURE", + "DOCKER", + "DOCKER", + "AWS", + "AWS", + "AWS", + "DOCKER", + "CI/CD", + "K8S", + "DEVOPS", + "FRONTEND", + "DEVOPS", + "LOGGING", + "CRYPTO", + "BINARY", + "BINARY", + "BINARY", + "FRONTEND", + "WEB3", + "WEB3", + "BINARY", + "TERRAFORM", + "GIT", + "LOGGING", + "FRONTEND", + "AI", + "DOCKER", + "PASSWORD", + "CRYPTO", + "DOCS", + "BINARY", + "CI/CD", + "GIT", + "CRYPTO", + "CRYPTO", + "PASSWORD", + "LOGGING", + "VAULT", + "VAULT", + "VAULT", + "VAULT", + "BINARY", + "BINARY", + "CRYPTO", + "DOCKER", + "BINARY", + "DOCKER", + "DOCKER", + "DOCKER", + "DOCKER", + "AI", + "AI", + "LOGGING", ] environments = [ - "Docker", "Docker", "Docker", "Docker", "AWS", "Azure", "Docker", "Docker", "AWS", "AWS", - "AWS", "Docker", "Docker", "K8s", "Docker", "Docker", "Docker", "Docker", "Docker", "Docker", - "Docker", "Docker", "Docker", "Docker", "Docker", "Docker", "AWS", "Docker", "Docker", "Docker", - "Docker", "K8s", "Docker", "Docker", "Docker", "Docker", "Docker", "Docker", "Docker", "Docker", - "Docker", "Docker", "K8s with Vault", "K8s with Vault", "K8s with Vault", "K8s with Vault", "Docker", "Docker", "Docker", "Docker", - "Docker", "Docker", "K8s", "Docker", "Docker", "Docker", "Docker", "Docker" + "Docker", + "Docker", + "Docker", + "Docker", + "AWS", + "Azure", + "Docker", + "Docker", + "AWS", + "AWS", + "AWS", + "Docker", + "Docker", + "K8s", + "Docker", + "Docker", + "Docker", + "Docker", + "Docker", + "Docker", + "Docker", + "Docker", + "Docker", + "Docker", + "Docker", + "Docker", + "AWS", + "Docker", + "Docker", + "Docker", + "Docker", + "K8s", + "Docker", + "Docker", + "Docker", + "Docker", + "Docker", + "Docker", + "Docker", + "Docker", + "Docker", + "Docker", + "K8s with Vault", + "K8s with Vault", + "K8s with Vault", + "K8s with Vault", + "Docker", + "Docker", + "Docker", + "Docker", + "Docker", + "Docker", + "K8s", + "Docker", + "Docker", + "Docker", + "Docker", + "Docker", ] for i, name in enumerate(challenge_names): diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57.java index ec5d86a20..aefcbfce0 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57.java @@ -1,6 +1,7 @@ package org.owasp.wrongsecrets.challenges.docker; import com.google.common.base.Strings; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import lombok.extern.slf4j.Slf4j; import org.owasp.wrongsecrets.challenges.Challenge; import org.owasp.wrongsecrets.challenges.Spoiler; @@ -30,6 +31,7 @@ public boolean answerCorrect(String answer) { * an exposed LLM API key. This demonstrates how sensitive API keys can be accidentally exposed in * client-side code. */ + @SuppressFBWarnings("VA_FORMAT_STRING_USES_NEWLINE") public String getLLMJavaScriptCode() { return """ // AI Chat Application - Client-side JavaScript From e4f177cfe6e149565117fa0c63464b4d0416a0c0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 3 Aug 2025 07:47:45 +0000 Subject: [PATCH 09/17] Fix HTML and adoc formatting issues and add ZAP whitelist rule Co-authored-by: commjoen <1457214+commjoen@users.noreply.github.com> --- .github/scripts/generate_thymeleaf_previews.py | 12 ++++++------ config/zap/rule-config.tsv | 1 + src/main/resources/explanations/challenge58.adoc | 2 +- .../resources/explanations/challenge58_hint.adoc | 2 +- .../resources/explanations/challenge58_reason.adoc | 2 +- static-site/pr-123/pages/challenge-57.html | 6 +++--- static-site/pr-123/pages/challenge-58.html | 10 +++++----- static-site/pr-123/pages/challenge-example.html | 4 ++-- static-site/pr-123/pages/stats.html | 2 +- static-site/pr-123/pages/welcome.html | 4 ++-- 10 files changed, 23 insertions(+), 22 deletions(-) diff --git a/.github/scripts/generate_thymeleaf_previews.py b/.github/scripts/generate_thymeleaf_previews.py index 746cda931..4df7510c1 100755 --- a/.github/scripts/generate_thymeleaf_previews.py +++ b/.github/scripts/generate_thymeleaf_previews.py @@ -599,7 +599,7 @@ def generate_challenge_57_preview(self):

📋 Challenge Description

This challenge demonstrates a critical security vulnerability in modern AI-powered web applications: LLM API keys exposed in client-side JavaScript code.

- +

As developers rush to integrate AI capabilities, many make the critical mistake of putting sensitive API credentials directly in browser-accessible code.

@@ -628,7 +628,7 @@ class LLMChatApp { this.apiEndpoint = 'https://api.example-llm.com/v1/chat/completions'; this.initializeChat(); } - + async sendMessage() { try { const response = await fetch(this.apiEndpoint, { @@ -727,7 +727,7 @@ def generate_challenge_58_preview(self):

📋 Challenge Description

This challenge demonstrates one of the most common and dangerous ways secrets leak in real-world applications: database connection strings with embedded credentials exposed through error messages.

- +

When applications fail to connect to databases, they often expose the full connection string (including usernames and passwords) in error messages, logs, or even user-facing interfaces.

@@ -759,13 +759,13 @@ def generate_challenge_58_preview(self): return "Connection successful"; } catch (SQLException e) { // Poor error handling - exposing full connection string - String errorMessage = + String errorMessage = "Database connection failed with connection string: " + DB_CONNECTION_STRING + "\nError: " + e.getMessage(); - + // Credentials also get logged (another exposure vector) log.error("Failed to connect to database: {}", errorMessage); - + return errorMessage; } } diff --git a/config/zap/rule-config.tsv b/config/zap/rule-config.tsv index 080e43759..8974a0bb6 100644 --- a/config/zap/rule-config.tsv +++ b/config/zap/rule-config.tsv @@ -18,3 +18,4 @@ 10003 IGNORE Vulnerable JS Library 90004 IGNORE Insufficient Site Isolation Against Spectre Vulnerability 2 IGNORE Private IP Disclosure +90022 IGNORE Application Error Disclosure diff --git a/src/main/resources/explanations/challenge58.adoc b/src/main/resources/explanations/challenge58.adoc index 2957c96c3..378fd5a45 100644 --- a/src/main/resources/explanations/challenge58.adoc +++ b/src/main/resources/explanations/challenge58.adoc @@ -30,4 +30,4 @@ Visit the `/error-demo/database-connection` endpoint to simulate a database conn Can you find the database password that gets exposed when the application tries to connect to the database? -**Hint:** Look for database connection error messages that reveal more than they should. \ No newline at end of file +**Hint:** Look for database connection error messages that reveal more than they should. diff --git a/src/main/resources/explanations/challenge58_hint.adoc b/src/main/resources/explanations/challenge58_hint.adoc index ca9a67eba..390c37037 100644 --- a/src/main/resources/explanations/challenge58_hint.adoc +++ b/src/main/resources/explanations/challenge58_hint.adoc @@ -22,4 +22,4 @@ This is one of the most common ways secrets leak in production: - Development environments with verbose error reporting - CI/CD pipelines where database connections fail -**Remember:** The goal is to find the database password that gets exposed in the error message when the connection fails. \ No newline at end of file +**Remember:** The goal is to find the database password that gets exposed in the error message when the connection fails. diff --git a/src/main/resources/explanations/challenge58_reason.adoc b/src/main/resources/explanations/challenge58_reason.adoc index 819590381..54ea7abca 100644 --- a/src/main/resources/explanations/challenge58_reason.adoc +++ b/src/main/resources/explanations/challenge58_reason.adoc @@ -37,4 +37,4 @@ This challenge exposes one of the most frequent and dangerous secret leakage sce **The Bottom Line:** -Database connection string exposure is preventable with proper secret management practices and careful error handling. This vulnerability type has led to countless production breaches and should never occur in well-designed applications. \ No newline at end of file +Database connection string exposure is preventable with proper secret management practices and careful error handling. This vulnerability type has led to countless production breaches and should never occur in well-designed applications. diff --git a/static-site/pr-123/pages/challenge-57.html b/static-site/pr-123/pages/challenge-57.html index 06dc2bc5e..d8c882d9f 100644 --- a/static-site/pr-123/pages/challenge-57.html +++ b/static-site/pr-123/pages/challenge-57.html @@ -35,7 +35,7 @@

🤖 Challenge 57: LLM API Key Exposure

📋 Challenge Description

This challenge demonstrates a critical security vulnerability in modern AI-powered web applications: LLM API keys exposed in client-side JavaScript code.

- +

As developers rush to integrate AI capabilities, many make the critical mistake of putting sensitive API credentials directly in browser-accessible code.

@@ -64,7 +64,7 @@

💻 Code Preview

this.apiEndpoint = 'https://api.example-llm.com/v1/chat/completions'; this.initializeChat(); } - + async sendMessage() { try { const response = await fetch(this.apiEndpoint, { @@ -121,4 +121,4 @@

🏆 Learning Objectives

- \ No newline at end of file + diff --git a/static-site/pr-123/pages/challenge-58.html b/static-site/pr-123/pages/challenge-58.html index 8d808d6f3..45bffd119 100644 --- a/static-site/pr-123/pages/challenge-58.html +++ b/static-site/pr-123/pages/challenge-58.html @@ -36,7 +36,7 @@

🗄️ Challenge 58: Database Connection String Exposure

📋 Challenge Description

This challenge demonstrates one of the most common and dangerous ways secrets leak in real-world applications: database connection strings with embedded credentials exposed through error messages.

- +

When applications fail to connect to databases, they often expose the full connection string (including usernames and passwords) in error messages, logs, or even user-facing interfaces.

@@ -68,14 +68,14 @@

💻 Vulnerable Code Example

return "Connection successful"; } catch (SQLException e) { // Poor error handling - exposing full connection string - String errorMessage = + String errorMessage = "Database connection failed with connection string: " + DB_CONNECTION_STRING + " Error: " + e.getMessage(); - + // Credentials also get logged (another exposure vector) log.error("Failed to connect to database: {}", errorMessage); - + return errorMessage; } } @@ -128,4 +128,4 @@

🏆 Learning Objectives

- \ No newline at end of file + diff --git a/static-site/pr-123/pages/challenge-example.html b/static-site/pr-123/pages/challenge-example.html index 7272568d4..fd3c6e0bb 100644 --- a/static-site/pr-123/pages/challenge-example.html +++ b/static-site/pr-123/pages/challenge-example.html @@ -76,14 +76,14 @@
🔍 Your Task
🔍 What's Wrong?
-
-
diff --git a/static-site/pr-123/pages/stats.html b/static-site/pr-123/pages/stats.html index 7c8f4ce6d..80abaa83c 100644 --- a/static-site/pr-123/pages/stats.html +++ b/static-site/pr-123/pages/stats.html @@ -42,7 +42,7 @@ CTF-mode enabled: False
Spoilers enabled: True
Springdoc enabled: True - Swagger UI enabled: True
Swagger uri:
diff --git a/static-site/pr-123/pages/welcome.html b/static-site/pr-123/pages/welcome.html index 4c3749efa..1e4cf7b4c 100644 --- a/static-site/pr-123/pages/welcome.html +++ b/static-site/pr-123/pages/welcome.html @@ -67,7 +67,7 @@
🎯 How to Play
- + 0 @@ -941,7 +941,7 @@
🎯 How to Play

Total score: 42

-