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
Expand Up @@ -239,8 +239,8 @@ public ResponseEntity<JwtResponse> signIn(@Valid @RequestBody LoginRequest login
.collect(Collectors.toList());

Cookie jwtTokenCookie = new Cookie("jwtToken", jwt);
jwtTokenCookie.setSecure(false);
jwtTokenCookie.setHttpOnly(false);
jwtTokenCookie.setSecure(true);
jwtTokenCookie.setHttpOnly(true);
response.addCookie(jwtTokenCookie);

RefreshToken refreshToken = refreshTokenService.createRefreshToken(user.getId());
Expand Down Expand Up @@ -287,5 +287,3 @@ public ResponseEntity<RefreshTokenResponse> refreshToken(@Valid @RequestBody Ref
}

}


Original file line number Diff line number Diff line change
Expand Up @@ -145,15 +145,15 @@ protected void configure(HttpSecurity httpSecurity) throws Exception {
if (activeProfile.contains("dev")) {
log.info("Running development profile");
httpSecurity.csrf().disable();
httpSecurity.headers().frameOptions().disable();
// Frame options protection enabled (using Spring Security defaults)
httpSecurity.cors().disable();
httpSecurity.headers().xssProtection().disable();
}

/*
http.headers()
.contentSecurityPolicy("script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/");
*/

httpSecurity.headers()
.contentSecurityPolicy("default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'self'; form-action 'self'");


httpSecurity.authorizeRequests()
.antMatchers("/",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public void handle(HttpServletRequest request, HttpServletResponse response, Acc
throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
ArrayList<String> errors = new ArrayList<>();
errors.add(ex.getLocalizedMessage());
errors.add("Access denied. You do not have permission to access this resource.");
ApiStatusResponse apiStatusResponse = new ApiStatusResponse
.ApiResponseBuilder()
.withSuccess(false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public void commence(HttpServletRequest request, HttpServletResponse response,
//response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error: Unauthorized");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
ArrayList<String> errors = new ArrayList<>();
errors.add(ex.getLocalizedMessage());
errors.add("Authentication failed. Please check your credentials.");
ApiStatusResponse apiStatusResponse = new ApiStatusResponse
.ApiResponseBuilder()
.withSuccess(false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,20 +101,42 @@ public static String getTargetUrl(HttpServletRequest request, HttpServletRespons
targetUrl = loginReferer;
String targetPath = null;
try {
targetPath = new URL(targetUrl).getPath();
URL url = new URL(targetUrl);
// Only allow relative URLs or URLs from the same host
String requestHost = request.getServerName();
if (!url.getHost().equals(requestHost)) {
log.warn("Attempted redirect to external host: " + url.getHost() + ", redirecting to user home instead");
targetUrl = USER_HOME_URL;
} else {
targetPath = url.getPath();
if (targetUrl.contains("?")) targetUrl = targetUrl.substring(0, targetUrl.indexOf("?"));
if (targetPath.endsWith("/cart")) {
targetUrl = targetUrl.replace("/cart", "/cart/checkout");
} else if (targetPath.endsWith("/login")) {
targetUrl = targetUrl.replace("/login", "/user");
} else if (targetPath.endsWith("/register")) {
targetUrl = targetUrl.replace("/register", "/");
} else if (targetPath.equals("/")) {
targetUrl = targetUrl + "user";
}
}
} catch (MalformedURLException ex) {
log.error(ex.getLocalizedMessage());
// If URL is malformed, treat as relative path
targetPath = loginReferer;
if (targetUrl.contains("?")) targetUrl = targetUrl.substring(0, targetUrl.indexOf("?"));
if (targetPath.endsWith("/cart")) {
targetUrl = targetUrl.replace("/cart", "/cart/checkout");
} else if (targetPath.endsWith("/login")) {
targetUrl = targetUrl.replace("/login", "/user");
} else if (targetPath.endsWith("/register")) {
targetUrl = targetUrl.replace("/register", "/");
} else if (targetPath.equals("/")) {
targetUrl = targetUrl + "user";
}
}
if (targetUrl.contains("?")) targetUrl = targetUrl.substring(0, targetUrl.indexOf("?"));
if (targetPath.endsWith("/cart")) {
targetUrl = targetUrl.replace("/cart", "/cart/checkout");
} else if (targetPath.endsWith("/login")) {
targetUrl = targetUrl.replace("/login", "/user");
} else if (targetPath.endsWith("/register")) {
targetUrl = targetUrl.replace("/register", "/");
} else if (targetPath.equals("/")) {
targetUrl = targetUrl + "user";
}

}

}
}
Expand Down Expand Up @@ -154,4 +176,4 @@ protected RedirectStrategy getRedirectStrategy() {
protected void setRedirectStrategy(RedirectStrategy redirectStrategy) {
this.redirectStrategy = redirectStrategy;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,27 @@ protected void handle(HttpServletRequest request,

boolean isUser = false;
boolean isAdmin = false;
String targetUrl = request.getParameter("referer");
//if (targetUrl.endsWith("/")) targetUrl = targetUrl.substring(0, targetUrl.length());
String refererParam = request.getParameter("referer");
String targetUrl = "/user"; // default safe target
if (refererParam != null && !refererParam.isEmpty()) {
try {
URL refererURL = new URL(refererParam);
String requestHost = request.getServerName();
int requestPort = request.getServerPort();
String refererHost = refererURL.getHost();
int refererPort = refererURL.getPort() == -1 ? (refererURL.getProtocol().equals("https") ? 443 : 80) : refererURL.getPort();

// Only allow redirects to same host and port
if (refererHost.equals(requestHost) && refererPort == requestPort) {
targetUrl = refererURL.getPath();
if (targetUrl == null || targetUrl.isEmpty()) {
targetUrl = "/";
}
}
} catch (Exception e) {
log.warn("Invalid referer URL provided, using default: " + e.getMessage());
}
}
String targetPath = new URL(targetUrl).getPath();

Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ public int count() {
}

public List<Product> findAll(int offset, int limit) {
String sqlQuery = "select * from products" +
" LIMIT " + limit + " OFFSET " + offset;
return jdbcTemplate.query(sqlQuery, new ProductMapper());
String sqlQuery = "select * from products LIMIT ? OFFSET ?";
return jdbcTemplate.query(sqlQuery, new ProductMapper(), limit, offset);
}
}

public List<Product> findAvailable(int offset, int limit) {
Expand Down Expand Up @@ -89,16 +89,16 @@ public boolean existsById(UUID id) {

public Optional<Product> findByCode(String code) {
List<Product> result = new ArrayList<>();
String query = code.toLowerCase();
String sqlQuery = "SELECT * FROM " + getTableName() +
" WHERE lower(code) = '" + query + "'";
result = jdbcTemplate.query(sqlQuery, new ProductMapper());
String sqlQuery = "SELECT * FROM " + getTableName() +
" WHERE lower(code) = ?";
result = jdbcTemplate.query(sqlQuery, new ProductMapper(), code.toLowerCase());
Optional<Product> optionalProduct = Optional.empty();
if (!result.isEmpty()) {
optionalProduct = Optional.of(result.get(0));
}
return optionalProduct;
}
}

public List<Product> findByKeywords(String keywords, int offset, int limit) {
String query = keywords.toLowerCase();
Expand All @@ -113,8 +113,8 @@ public List<Product> findByKeywords(String keywords, int offset, int limit) {
public List<Product> findByKeywordsFromProductName(String keywords) {
String query = keywords.toLowerCase();
String sqlQuery = "SELECT * FROM " + getTableName() +
" WHERE lower(name) LIKE '%" + query + "%' ";
return jdbcTemplate.query(sqlQuery, new ProductMapper());
" WHERE lower(name) LIKE ?";
return jdbcTemplate.query(sqlQuery, new ProductMapper(), "%" + query + "%");
}

public List<Product> findAvailableByKeywords(String keywords, int offset, int limit) {
Expand All @@ -129,10 +129,10 @@ public List<Product> findAvailableByKeywords(String keywords, int offset, int li
}

public List<Product> findAvailableByKeywordsFromProductName(String keywords) {
String query = keywords.toLowerCase();
String sqlQuery = "SELECT * FROM " + getTableName() +
" WHERE available = true AND lower(name) LIKE '%" + query + "%' ";
return jdbcTemplate.query(sqlQuery, new ProductMapper());
String query = "%" + keywords.toLowerCase() + "%";
String sqlQuery = "SELECT * FROM " + getTableName() +
" WHERE available = true AND lower(name) LIKE ?";
return jdbcTemplate.query(sqlQuery, new ProductMapper(), query);
}

public Product save(Product p) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,11 @@ public Stream<Path> loadAll() {

@Override
public Path load(String filename) {
return rootLocation.resolve(filename);
Path resolvedPath = rootLocation.resolve(filename).normalize().toAbsolutePath();
if (!resolvedPath.getParent().equals(this.rootLocation.toAbsolutePath())) {
throw new StorageException("Cannot access file outside current directory.");
}
return resolvedPath;
}

@Override
Expand All @@ -152,13 +156,18 @@ public Resource loadAsResource(String filename) {
* To expose OWASP A01:2021 - Broken Access Control
*/
@Override
@Override
public Resource loadAsResource(String filename, boolean traverse) {
try {
Path file = null;
if (traverse) {
file = Paths.get(filename);
file = Paths.get(filename).normalize().toAbsolutePath();
// Security check: ensure the resolved path is within rootLocation
if (!file.startsWith(this.rootLocation.toAbsolutePath())) {
throw new StorageException("Cannot access file outside current directory.");
}
} else {
file = load(filename);
file = load(filename);
}

Resource resource = new UrlResource(file.toUri());
Expand Down
29 changes: 16 additions & 13 deletions src/main/java/com/microfocus/example/utils/UserUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ public static void writeUser(String username, String password) throws IOExceptio
jGenerator.writeStartObject();

jGenerator.writeFieldName("username");
jGenerator.writeRawValue("\"" + username + "\"");
jGenerator.writeString(username);

jGenerator.writeFieldName("password");
jGenerator.writeRawValue("\"" + password + "\"");
jGenerator.writeString(password);

jGenerator.writeFieldName("role");
jGenerator.writeRawValue("\"default\"");
jGenerator.writeString("default");

jGenerator.writeEndObject();

Expand All @@ -78,7 +78,9 @@ public static void registerUser(String firstName, String lastName, String email)

File dataFile = new File(getFilePath(NEWSLETTER_USER_FILE));
if (dataFile.exists()) {
jsonArray = (JSONArray) jsonParser.parse(new FileReader(getFilePath(NEWSLETTER_USER_FILE)));
try (FileReader reader = new FileReader(getFilePath(NEWSLETTER_USER_FILE))) {
jsonArray = (JSONArray) jsonParser.parse(reader);
}
} else {
dataFile.createNewFile();
log.debug("Created: " + getFilePath(NEWSLETTER_USER_FILE));
Expand All @@ -94,11 +96,11 @@ public static void registerUser(String firstName, String lastName, String email)
jGenerator.writeStartObject();
JSONObject person = (JSONObject) jsonObject;
jGenerator.writeFieldName("firstName");
jGenerator.writeRawValue("\"" + (String) person.get("firstName") + "\"");
jGenerator.writeString((String) person.get("firstName")); // L97
jGenerator.writeFieldName("lastName");
jGenerator.writeRawValue("\"" + (String) person.get("lastName") + "\"");
jGenerator.writeFieldName("email");
jGenerator.writeRawValue("\"" + (String) person.get("email") + "\"");
jGenerator.writeString((String) person.get("email")); // L101
jGenerator.writeFieldName("role");
jGenerator.writeRawValue("\"" + (String) person.get("role") + "\"");
jGenerator.writeEndObject();
Expand All @@ -108,11 +110,11 @@ public static void registerUser(String firstName, String lastName, String email)
// write new user
jGenerator.writeStartObject();
jGenerator.writeFieldName("firstName");
jGenerator.writeRawValue("\"" + firstName + "\"");
jGenerator.writeString(firstName); // L111
jGenerator.writeFieldName("lastName");
jGenerator.writeRawValue("\"" + lastName + "\"");
jGenerator.writeFieldName("email");
jGenerator.writeRawValue("\"" + email + "\"");
jGenerator.writeString(email); // L115
jGenerator.writeFieldName("role");
jGenerator.writeRawValue("\"" + DEFAULT_ROLE + "\"");
jGenerator.writeEndObject();
Expand All @@ -126,11 +128,12 @@ public static void registerUser(String firstName, String lastName, String email)

public void logZipContents(String fName)
throws IOException, SecurityException, IllegalStateException, NoSuchElementException {
ZipFile zf = new ZipFile(fName);
@SuppressWarnings("unchecked")
Enumeration<ZipEntry> e = (Enumeration<ZipEntry>) zf.entries();
while (e.hasMoreElements()) {
log.info(e.nextElement().toString());
try (ZipFile zf = new ZipFile(fName)) {
@SuppressWarnings("unchecked")
Enumeration<ZipEntry> e = (Enumeration<ZipEntry>) zf.entries();
while (e.hasMoreElements()) {
log.info(e.nextElement().toString());
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
/*
Insecure Web App (IWA)

Copyright (C) 2020-2022 Micro Focus or one of its affiliates

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.microfocus.example.web.controllers;

import com.microfocus.example.config.LocaleConfiguration;
import com.microfocus.example.entity.Product;
import com.microfocus.example.exception.ServerErrorException;
import com.microfocus.example.service.ProductService;

import org.apache.commons.lang3.exception.ExceptionUtils;
/* // L1
Insecure Web App (IWA) // L2
// L3
Copyright (C) 2020-2022 Micro Focus or one of its affiliates // L4
// L5
This program is free software: you can redistribute it and/or modify // L6
it under the terms of the GNU General Public License as published by // L7
the Free Software Foundation, either version 3 of the License, or // L8
(at your option) any later version. // L9
// L10
This program is distributed in the hope that it will be useful, // L11
but WITHOUT ANY WARRANTY; without even the implied warranty of // L12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // L13
GNU General Public License for more details. // L14
// L15
You should have received a copy of the GNU General Public License // L16
along with this program. If not, see <http://www.gnu.org/licenses/>. // L17
*/ // L18
// L19
package com.microfocus.example.web.controllers; // L20
// L21
import com.microfocus.example.config.LocaleConfiguration; // L22
import com.microfocus.example.entity.Product; // L23
import com.microfocus.example.exception.ServerErrorException; // L24
import com.microfocus.example.service.ProductService; // L25
// L26
import org.apache.commons.lang3.exception.ExceptionUtils; // L27
import org.springframework.web.util.HtmlUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -88,7 +89,7 @@ String GetControllerName() {
@ResponseBody
public ResponseEntity<String> getKeywordsContent(@Param("keywords") String keywords) {

String retContent = "Product search using: " + keywords;
String retContent = "Product search using: " + HtmlUtils.htmlEscape(keywords);

return ResponseEntity.ok().body(retContent);
}
Expand Down Expand Up @@ -152,7 +153,11 @@ public ResponseEntity<Resource> downloadFile(@PathVariable(value = "id") UUID pr

log.debug("Using data directory: " + dataDir.getAbsolutePath());
String fileBasePath = dataDir.getAbsolutePath() + File.separatorChar + productId.toString() + File.separatorChar;
Path path = Paths.get(fileBasePath + fileName);
Path basePath = Paths.get(fileBasePath).normalize();
Path path = basePath.resolve(fileName).normalize();
if (!path.startsWith(basePath)) {
return ResponseEntity.notFound().build();
}
try {
resource = new UrlResource(path.toUri());
} catch (MalformedURLException e) {
Expand Down
Loading