Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/base}">
<head>
<title th:text="#{change.title}">Password Expired</title>
<title th:text="#{change.title}">Password Has Expired</title>
</head>
<body>
<h1 th:text="#{change.title}">Password Has Expired</h1>
<p th:text="#{change.message}">Your password has expired.</p>
<p th:text="${username}">username</p>
<a th:href="@{/editLogon}" th:text="#{change.try}">Try Again</a>
<div layout:fragment="menu">
<div th:replace="~{fragments/loggedoff-menu :: menu}"></div>
</div>
<div layout:fragment="content">
<p th:text="#{change.message}">Your password has expired. Please ask the system administrator to change it.</p>
<a th:href="@{/editLogon}" th:text="#{change.try}">Try Again</a>
</div>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div th:fragment="footer">
<span th:text="#{footer.value}">Copyright (C) 1999-2004, The Apache Software Foundation</span>
</div>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div th:fragment="header">
<span th:text="#{header.value}">Struts + Faces + Tiles</span>
</div>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div th:fragment="menu">
<a th:href="@{/editRegistration(action='Create')}" th:text="#{loggedoff.register}">Register</a>
<br />
<a th:href="@{/editLogon}" th:text="#{loggedoff.logon}">Log On</a>
</div>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div th:fragment="menu">
<a th:href="@{/editRegistration(action='Edit')}" th:text="#{mainMenu.registration}">Edit Registration</a>
<br />
<a th:href="@{/logoff}" th:text="#{loggedon.logoff}">Log Off</a>
</div>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<title layout:title-pattern="$CONTENT_TITLE - $LAYOUT_TITLE" th:text="#{layout.title}">Struts+Tiles+Faces Example Application</title>
<link th:href="@{/css/stylesheet.css}" rel="stylesheet" />
</head>
<body>
<table border="1" width="100%" cellspacing="5">
<tr>
<th colspan="2" align="center">
<div th:replace="~{fragments/header :: header}"></div>
</th>
</tr>
<tr>
<td width="140" valign="top">
<div layout:fragment="menu"></div>
</td>
<td align="left" valign="top">
<div layout:fragment="content"></div>
</td>
</tr>
<tr>
<td colspan="2" align="center">
<div th:replace="~{fragments/footer :: footer}"></div>
</td>
</tr>
</table>
</body>
</html>
61 changes: 44 additions & 17 deletions apps/example2-spring-boot/src/main/resources/templates/logon.html
Original file line number Diff line number Diff line change
@@ -1,25 +1,52 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/base}">
<head>
<title th:text="#{logon.title}">Logon</title>
</head>
<body>
<h1 th:text="#{logon.header}">Logon Form</h1>
<div th:if="${#fields.hasGlobalErrors()}" th:each="err : ${#fields.globalErrors()}">
<span th:text="${err}" class="error">Error</span>
<div layout:fragment="menu">
<div th:replace="~{fragments/loggedoff-menu :: menu}"></div>
</div>
<div layout:fragment="content">
<form th:action="@{/logon}" th:object="${logonForm}" method="post">
<table class="form-background">
<tr>
<th colspan="2" class="form-header" th:text="#{logon.header}">Logon Form</th>
</tr>
<tr>
<td class="form-prompt"><label for="username" th:text="#{prompt.username}">Username</label></td>
<td class="form-field">
<input type="text" th:field="*{username}" id="username" size="16" />
<span th:if="${#fields.hasErrors('username')}" th:errors="*{username}" class="error"></span>
</td>
</tr>
<tr>
<td class="form-prompt"><label for="password" th:text="#{prompt.password}">Password</label></td>
<td class="form-field">
<input type="password" th:field="*{password}" id="password" size="16" />
<span th:if="${#fields.hasErrors('password')}" th:errors="*{password}" class="error"></span>
</td>
</tr>
<tr th:if="${#fields.hasGlobalErrors()}">
<td colspan="2" class="form-field">
<span th:each="err : ${#fields.globalErrors()}" th:text="${err}" class="error"></span>
</td>
</tr>
<tr>
<td class="form-field">
<button type="submit" class="command-single">Log On</button>
</td>
<td class="form-field">
<button type="reset" class="command-single">Reset</button>
</td>
</tr>
<tr>
<td colspan="2" class="form-footer" th:text="#{logon.footer}">Enter your username and password</td>
</tr>
</table>
</form>
</div>
<form th:action="@{/logon}" th:object="${logonForm}" method="post">
<div>
<label th:text="#{prompt.username}">Username</label>
<input type="text" th:field="*{username}" />
<span th:if="${#fields.hasErrors('username')}" th:errors="*{username}" class="error">Username Error</span>
</div>
<div>
<label th:text="#{prompt.password}">Password</label>
<input type="password" th:field="*{password}" />
<span th:if="${#fields.hasErrors('password')}" th:errors="*{password}" class="error">Password Error</span>
</div>
<button type="submit" th:text="#{button.save}">Log On</button>
</form>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/base}">
<head>
<title th:text="#{mainMenu.title}">Main Menu</title>
</head>
<body>
<h1 th:text="#{mainMenu.heading}">Main Menu Options</h1>
<ul>
<li><a th:href="@{/editRegistration?action=Edit}" th:text="#{mainMenu.registration}">Edit Registration</a></li>
<li><a th:href="@{/logoff}" th:text="#{mainMenu.logoff}">Log Off</a></li>
</ul>
<div layout:fragment="menu">
<div th:replace="~{fragments/loggedon-menu :: menu}"></div>
</div>
<div layout:fragment="content">
<h3>
<span th:text="#{mainMenu.heading}">Main Menu Options for </span>
<span th:text="${session.user.username}">username</span>
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 mainMenu.html accesses session.user.username without null guard, but controller has no login check

The mainMenu.html template at line 15 accesses ${session.user.username}, but the WelcomeController.mainMenu() method does not verify the user is logged in before rendering the template.

Root Cause

The WelcomeController.mainMenu() at controller/WelcomeController.java:37-40 simply returns the "mainMenu" view name without checking if a user exists in the session:

@GetMapping("/mainMenu")
public String mainMenu() {
    return "mainMenu";
}

The template at mainMenu.html:15 then accesses ${session.user.username}. If a user navigates directly to /mainMenu without being logged in, session.user will be null, causing a Thymeleaf template evaluation error (equivalent to a NullPointerException) when trying to access .username on a null object.

Impact: Unauthenticated users visiting /mainMenu will see a 500 error page instead of being redirected to the login page.

Prompt for agents
The mainMenu.html template at line 15 accesses ${session.user.username} but the WelcomeController.mainMenu() method at apps/example2-spring-boot/src/main/java/org/apache/struts/webapp/example2/controller/WelcomeController.java:37-40 does not check if the user is logged in. Add a session check to the mainMenu() controller method similar to what RegistrationController does: retrieve the user from session using Constants.USER_KEY, and if null, redirect to the logon page. The method signature needs to accept HttpSession as a parameter.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

</h3>
<ul>
<li><a th:href="@{/editRegistration(action='Edit')}" th:text="#{mainMenu.registration}">Edit your user registration profile</a></li>
<li><a th:href="@{/logoff}" th:text="#{mainMenu.logoff}">Log off MailReader Demonstration Application</a></li>
</ul>
</div>
</body>
</html>
153 changes: 114 additions & 39 deletions apps/example2-spring-boot/src/main/resources/templates/registration.html
Original file line number Diff line number Diff line change
@@ -1,46 +1,121 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/base}">
<head>
<title>Registration</title>
<title th:if="${registrationForm.action == 'Create'}" th:text="#{registration.title.create}">Register</title>
<title th:if="${registrationForm.action == 'Edit'}" th:text="#{registration.title.edit}">Edit Registration</title>
</head>
<body>
<h1>Registration</h1>
<form th:action="@{/saveRegistration}" th:object="${registrationForm}" method="post">
<input type="hidden" th:field="*{action}" />
<div>
<label th:text="#{prompt.username}">Username</label>
<input type="text" th:field="*{username}" />
<span th:if="${#fields.hasErrors('username')}" th:errors="*{username}"></span>
<div layout:fragment="menu">
<th:block th:if="${session.user != null}">
<div th:replace="~{fragments/loggedon-menu :: menu}"></div>
</th:block>
<th:block th:if="${session.user == null}">
<div th:replace="~{fragments/loggedoff-menu :: menu}"></div>
</th:block>
</div>
<div layout:fragment="content">
<form th:action="@{/saveRegistration}" th:object="${registrationForm}" method="post">
<input type="hidden" th:field="*{action}" />
<table class="form-background">
<tr>
<th colspan="2" class="form-header">
<span th:if="${registrationForm.action == 'Create'}" th:text="#{registration.header.create}">Enter Registration Information</span>
<span th:if="${registrationForm.action == 'Edit'}" th:text="#{registration.header.edit}">Edit Your Registration Information</span>
</th>
</tr>
<tr>
<td class="form-prompt"><label for="username" th:text="#{prompt.username}">Username</label></td>
<td class="form-field">
<span th:if="${registrationForm.action == 'Create'}">
<input type="text" th:field="*{username}" id="username" size="16" />
</span>
<span th:if="${registrationForm.action == 'Edit'}">
<span th:text="*{username}">username</span>
<input type="hidden" th:field="*{username}" />
</span>
<span th:if="${#fields.hasErrors('username')}" th:errors="*{username}" class="error"></span>
</td>
</tr>
<tr>
<td class="form-prompt"><label for="password" th:text="#{prompt.password}">Password</label></td>
<td class="form-field">
<input type="text" th:field="*{password}" id="password" size="16" />
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Password fields changed from type="password" to type="text" in registration form

The password and password confirmation fields in registration.html were changed from type="password" to type="text", causing passwords to be displayed in plain text on screen.

Security Impact

The old code used type="password" for both password fields (visible in the diff's left side at lines 32 and 37), which masks the input. The new code at lines 44 and 51 uses type="text", which renders the password as visible plain text. This is a security issue as passwords will be visible to anyone who can see the user's screen (shoulder surfing), and may also be captured by browser auto-fill or screen recording software.

Actual: Password input is displayed as plain text (type="text").
Expected: Password input should be masked (type="password").

Suggested change
<input type="text" th:field="*{password}" id="password" size="16" />
<input type="password" th:field="*{password}" id="password" size="16" />
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

<span th:if="${#fields.hasErrors('password')}" th:errors="*{password}" class="error"></span>
</td>
</tr>
<tr>
<td class="form-prompt"><label for="password2" th:text="#{prompt.password2}">(Repeat) Password</label></td>
<td class="form-field">
<input type="text" th:field="*{password2}" id="password2" size="16" />
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Password confirmation field changed from type="password" to type="text" in registration form

The password2 (confirmation) field in registration.html was changed from type="password" to type="text", causing the repeated password to be displayed in plain text.

Security Impact

Same issue as the password field — the old code at diff left side line 37 used type="password", but the new code at line 51 uses type="text". This exposes the confirmation password entry to shoulder surfing.

Suggested change
<input type="text" th:field="*{password2}" id="password2" size="16" />
<input type="password" th:field="*{password2}" id="password2" size="16" />
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

<span th:if="${#fields.hasErrors('password2')}" th:errors="*{password2}" class="error"></span>
</td>
</tr>
<tr>
<td class="form-prompt"><label for="fullName" th:text="#{prompt.fullName}">Full Name</label></td>
<td class="form-field">
<input type="text" th:field="*{fullName}" id="fullName" size="50" />
<span th:if="${#fields.hasErrors('fullName')}" th:errors="*{fullName}" class="error"></span>
</td>
</tr>
<tr>
<td class="form-prompt"><label for="fromAddress" th:text="#{prompt.fromAddress}">From Address</label></td>
<td class="form-field">
<input type="text" th:field="*{fromAddress}" id="fromAddress" size="50" />
<span th:if="${#fields.hasErrors('fromAddress')}" th:errors="*{fromAddress}" class="error"></span>
</td>
</tr>
<tr>
<td class="form-prompt"><label for="replyToAddress" th:text="#{prompt.replyToAddress}">Reply To Address</label></td>
<td class="form-field">
<input type="text" th:field="*{replyToAddress}" id="replyToAddress" size="50" />
<span th:if="${#fields.hasErrors('replyToAddress')}" th:errors="*{replyToAddress}" class="error"></span>
</td>
</tr>
<tr th:if="${#fields.hasGlobalErrors()}">
<td colspan="2" class="form-field">
<span th:each="err : ${#fields.globalErrors()}" th:text="${err}" class="error"></span>
</td>
</tr>
<tr>
<td class="form-field">
<button type="submit" class="command-single" th:text="#{button.save}">Save</button>
</td>
<td class="form-field">
<button type="reset" class="command-multiple" th:text="#{button.reset}">Reset</button>
<a th:href="@{/mainMenu}" class="command-multiple" th:text="#{button.cancel}">Cancel</a>
</td>
</tr>
</table>
</form>

<div th:if="${registrationForm.action == 'Edit' and session.user != null}">
<h3 th:text="#{heading.subscriptions}">Current Subscriptions</h3>
<table class="list-background">
<tr class="list-header">
<th th:text="#{heading.host}">Host Name</th>
<th th:text="#{heading.user}">User Name</th>
<th th:text="#{heading.type}">Server Type</th>
<th th:text="#{heading.autoConnect}">Auto</th>
<th th:text="#{heading.action}">Action</th>
</tr>
<tr th:each="subscription, iterStat : ${session.user.subscriptions}"
th:class="${iterStat.odd ? 'list-row-odd' : 'list-row-even'}">
<td class="list-column-host" th:text="${subscription.host}">host</td>
<td class="list-column-user" th:text="${subscription.username}">username</td>
<td class="list-column-type" th:text="${subscription.type}">type</td>
<td class="list-column-auto" th:text="${subscription.autoConnect}">false</td>
<td class="list-column-action">
<a th:href="@{/editSubscription(action='Delete',host=${subscription.host})}"
class="command-multiple" th:text="#{registration.deleteSubscription}">Delete</a>
<a th:href="@{/editSubscription(action='Edit',host=${subscription.host})}"
class="command-multiple" th:text="#{registration.editSubscription}">Edit</a>
</td>
</tr>
</table>
<a th:href="@{/editSubscription(action='Create')}" th:text="#{registration.addSubscription}">Add</a>
</div>
<div>
<label th:text="#{prompt.fullName}">Full Name</label>
<input type="text" th:field="*{fullName}" />
<span th:if="${#fields.hasErrors('fullName')}" th:errors="*{fullName}"></span>
</div>
<div>
<label th:text="#{prompt.fromAddress}">From Address</label>
<input type="text" th:field="*{fromAddress}" />
<span th:if="${#fields.hasErrors('fromAddress')}" th:errors="*{fromAddress}"></span>
</div>
<div>
<label th:text="#{prompt.replyToAddress}">Reply To Address</label>
<input type="text" th:field="*{replyToAddress}" />
<span th:if="${#fields.hasErrors('replyToAddress')}" th:errors="*{replyToAddress}"></span>
</div>
<div>
<label th:text="#{prompt.password}">Password</label>
<input type="password" th:field="*{password}" />
<span th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></span>
</div>
<div>
<label th:text="#{prompt.password2}">(Repeat) Password</label>
<input type="password" th:field="*{password2}" />
<span th:if="${#fields.hasErrors('password2')}" th:errors="*{password2}"></span>
</div>
<div>
<button type="submit" th:text="#{button.save}">Save</button>
<button type="reset" th:text="#{button.reset}">Reset</button>
</div>
</form>
</div>
</body>
</html>
Loading