This application contains intentional security vulnerabilities and should NEVER be deployed to production or exposed to the internet.
This is for educational and demonstration purposes only.
This is a simple Spring Boot application that demonstrates various security vulnerabilities that can be detected by application security testing tools such as provided by OpenText Application Security.
- Java 17
- Spring Boot 3.2.1
- Spring Data JPA
- H2 In-Memory Database
- Gradle 8.7
- React
- Tailwind CSS
This application includes the following intentional security vulnerabilities:
- Unparameterized SQL queries in
UserService - Direct concatenation of user input in SQL statements
- File operations without path validation in
FileService - Allows reading/writing arbitrary files on the system
- Direct execution of user-supplied commands in
FileServiceNo input validation or sanitization
- Unescaped user input reflected in HTML responses
- No output encoding in
UserController
- Hardcoded API keys in source code
- Hardcoded database credentials in
application.properties - Exposed secrets in configuration files
- Use of MD5 for password hashing (cryptographically broken)
- Plain text password storage
- Exposure of stack traces to users
- Sensitive credentials exposed via API endpoints
- Database credentials accessible through debug endpoints
- Weak password validation
- Plain text password comparison
- Password echoed back in login response
- Storing full payment card PAN and CVV in plain text (
Paymententity) - PCI/PII violation (INSECURE, demo only) - Debug endpoints that return or log raw card numbers (
/api/payments/debug/rawcards) — information disclosure - No input validation or sanitization on payment inputs (allows malformed/attacker-controlled values)
- Missing access controls and audit for payment operations (debug endpoints expose sensitive data even with minimal auth)
- No encryption or tokenization for payment data at rest or in transit beyond default TLS (demo lacks proper PCI controls)
# with a local Gradle installation
gradle clean build
# or using the Gradle wrapper (recommended if present)
./gradlew clean build# with a local Gradle installation
gradle bootRun
# or using the Gradle wrapper (recommended if present)
./gradlew bootRun
# or run the jar file:
java -jar build/libs/fortify-demo-app-1.0.0-SNAPSHOT.jar
# or if you have Docker installed
docker build .
docker run -d --name fortify-demo-app -p 8080:8080 fortify-demo-app:latestThe application will start on http://localhost:8080, you can browse to the frontend at this address or the Backend API at http://localhost:8080/swagger/index.html.
If you wish to develop new features for the application you can start the backend and frontend up separately. To start the backend (without frontend):
./gradlew clean bootRun -PskipFrontend=true
Then to start the frontend:
cd frontend
npm run dev
Note: if you make changes to the frontend the vite server will automatically reload. However, for changes to the backend you will need to stop and start the backend.
GET /api/users- Get all usersGET /api/users/search?query={query}- Search users (SQL Injection vulnerable)GET /api/users/find?username={username}- Find user (SQL Injection vulnerable)POST /api/users- Create new user (stores plaintext password — INSECURE demo)PUT /api/users/{id}- Update user (demo-only)POST /api/users/login?username={username}&password={password}- Login (returns demo JWT)POST /api/users/logout- Logout (blacklist provided token)GET /api/users/welcome?name={name}- Welcome page (XSS vulnerable)GET /api/users/{id}/profile?message={message}- User profile (XSS vulnerable)GET /api/users/debug/credentials- Expose database credentials (INSECURE)
GET /api/files/read?filename={filename}- Read file (Path Traversal vulnerable)POST /api/files/write?filename={filename}- Write file (Path Traversal vulnerable)GET /api/files/exec?cmd={cmd}- Execute command (Command Injection vulnerable)GET /api/files/shell?input={input}- Execute shell command (Command Injection vulnerable)GET /api/files/readabs?path={path}- Read absolute path (Path Traversal vulnerable)DELETE /api/files/delete?filename={filename}- Delete file (Path Traversal vulnerable)
GET /api/payments- Get all payments (exposes card data)GET /api/payments/user/{userId}- Get payments for a userPOST /api/payments- Create a payment method (stores card number/CVV in plain text)DELETE /api/payments/{id}- Delete a payment methodPOST /api/payments/charge?paymentId={id}&amount={amt}- Simulate charging a payment (debug/demo)GET /api/payments/debug/rawcards- Debug endpoint returning raw card numbers (INSECURE)
GET /api/transactions/payment/{paymentId}- Get transactions for a given payment
http://localhost:8080/h2-console- H2 Database Console
After starting the application (see Running the Application), the OpenAPI JSON and Swagger UI are available at:
- Swagger UI:
http://localhost:8080/swagger-ui/index.html - OpenAPI JSON:
http://localhost:8080/v3/api-docs
Notes:
- These docs describe the intentionally insecure endpoints in this demo application.
- If you change the server port, update the host/port in the URLs above accordingly.
The /api/users/login endpoint returns a raw JWT token on successful authentication. Use the token in an Authorization: Bearer <token> header to call protected endpoints (all /api/** except /api/users/login and /api/users/debug/credentials).
Examples (replace username/password with valid demo credentials):
# obtain token
TOKEN=$(curl -s -X POST "http://localhost:8080/api/users/login?username=alice&password=alice456")
echo "Token: $TOKEN"
# call a protected endpoint
curl -s -H "Authorization: Bearer $TOKEN" "http://localhost:8080/api/users"# obtain token
$token = Invoke-RestMethod -Method Post -Uri "http://localhost:8080/api/users/login?username=alice&password=alice456"
Write-Host "Token: $token"
# call a protected endpoint
Invoke-RestMethod -Uri "http://localhost:8080/api/users" -Headers @{ Authorization = "Bearer $token" }Notes:
- The token returned by the demo is intentionally insecure (hard-coded secret and demo claims). Do not reuse in production.
- If your server port differs, update the URLs accordingly.
Seeded demo users:
alice/alice456(see src/main/java/com/opentext/appsec/demo/DataInitializer.java)
You can test the REST API interactively with Postman or run the collection from the command line using newman.
Prerequisites:
- Node.js (v14+)
Run the included Postman collection (uses {{baseUrl}} collection variable, default http://localhost:8080):
# run with npx (no global install required)
npx newman run postman/FortifyDemoApp.postman_collection.jsonIf you want HTML output (install reporter locally or use npx):
npx newman run postman/FortifyDemoApp.postman_collection.json -r cli,html
# The HTML report will be written to the current folder (newman-run-report.html)Notes:
- The
Auth - Loginrequest uses the seededadmin/admin123credentials and stores the JWT in a collection variable namedtoken. - Subsequent requests use the header
Authorization: Bearer {{token}}.
This application is designed to be scanned with OpenText Application Security's SAST, SCA and DAST engines as well as AI remediation using Aviator.
Most of the vulnerabilities described above should be detected during static analysis.
You can use the Postman collection provided to run a DAST API scan.
You can use the Login macro provided to run a DAST Website scan.
Note: the Login macro above sets the Logout condition URL to the custom logout endpoint used by this app:
[URI]/api/users/logout
This tells the scanner the application logout location so it can detect end-of-session events.
The security scan should identify:
- Multiple SQL Injection vulnerabilities
- Path Traversal vulnerabilities
- Command Injection vulnerabilities
- Cross-Site Scripting (XSS) vulnerabilities
- Hardcoded credentials and API keys
- Weak cryptographic algorithms
- Information disclosure issues
- Insecure authentication mechanisms
- Insecure storage and exposure of payment card data (plain-text PAN/CVV) — PCI/PII issues
- Endpoints that deliberately log or reflect sensitive payment data (information disclosure)
This project is for demonstration purposes only. See LICENSE file for additional details.