This is the security-hardened PromptVault application for COMP47910 Assignment 3. It uses Java 21, Spring Boot 3.5.16, Thymeleaf, Spring Security, Spring Data JPA, and MySQL 8.
- Server-side role and prompt-ownership checks
- Allow-list form DTOs; IDs, owners, roles, flags, and account state are not bound
- BCrypt password hashing and a 12-character registration password policy
- Deployment-provided database and bootstrap credentials
- Five-failure login throttling and per-user prompt-submission throttling
- Immediate session revocation when an account is disabled
- CSRF protection, strict sessions, CSP, HSTS, anti-framing, and browser headers
- Bounded form inputs, encoded Thymeleaf output, and parameterized JPA persistence
- Security audit events with control-character neutralization and no prompt/password data
- Fail-secure policy checks, safe error responses, transactions, and optimistic locking
- Patched Logback, Jackson, and Tomcat; the unused Log4j bridge is removed
- Java 21 or newer
- Docker Desktop or MySQL 8
Create a MySQL container with a unique root password. Do not reuse this password for the application account.
docker run --name promptvault-mysql-a3 -e MYSQL_ROOT_PASSWORD=<unique-root-password> -p 3307:3306 -d mysql:8
docker cp .\db\promptvault.sql promptvault-mysql-a3:/tmp/promptvault.sql
docker exec -it promptvault-mysql-a3 mysql -u root -pAt the MySQL prompt, enter the root password and run:
SOURCE /tmp/promptvault.sql;
CREATE USER 'promptvault_app'@'%' IDENTIFIED BY '<different-unique-password>';
GRANT SELECT, INSERT, UPDATE, DELETE ON promptvault.* TO 'promptvault_app'@'%';
FLUSH PRIVILEGES;The schema script contains no accounts, password hashes, or sample prompt content. The runtime account has data permissions only and cannot alter the schema.
Set secrets without placing them directly in PowerShell history:
$env:DB_URL = "jdbc:mysql://localhost:3307/promptvault?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"
$env:DB_USERNAME = "promptvault_app"
$dbSecret = Read-Host "Database password" -AsSecureString
$env:DB_PASSWORD = [Net.NetworkCredential]::new("", $dbSecret).Password
$env:ADMIN_USERNAME = "administrator"
$adminSecret = Read-Host "Initial administrator password (15+ characters)" -AsSecureString
$env:ADMIN_PASSWORD = [Net.NetworkCredential]::new("", $adminSecret).Password
.\mvnw.cmd spring-boot:run "-Dspring-boot.run.profiles=dev"Open http://localhost:8080. The development profile permits local HTTP, enables
schema updates, and disables template caching. The administrator username and
password come from the environment. Normal users register through the application.
To include Alice and Bob sample accounts on a fresh database, also set
SEED_SAMPLE_USERS=true, ALICE_PASSWORD, and BOB_PASSWORD. Both passwords
must contain at least 15 characters. They have no defaults.
For an existing Assignment 1 database, set ADMIN_USERNAME to the existing admin
username. On startup, PromptVault replaces that account's old password with the
environment-provided value. The development profile also adds the prompt version
column required for optimistic locking.
The default profile is fail-safe:
DB_URL,DB_USERNAME,DB_PASSWORD,ADMIN_USERNAME, andADMIN_PASSWORDare required.- HTTPS redirection, secure cookies, HSTS, template caching, and schema validation are enabled.
- Deploy behind a trusted TLS reverse proxy and preserve forwarded headers.
- Apply
db/promptvault.sqlor a reviewed migration before startup. - Set
SEED_DATA=falseafter bootstrap if deployment-managed provisioning is used.
.\mvnw.cmd clean test
.\mvnw.cmd package
.\mvnw.cmd dependency:tree "-Dincludes=ch.qos.logback:logback-core,com.fasterxml.jackson.core:jackson-databind,org.apache.tomcat.embed:tomcat-embed-core,org.apache.logging.log4j:log4j-api"Tests use an isolated in-memory H2 database and do not require database credentials or a MySQL instance.