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
135 changes: 107 additions & 28 deletions src/main/java/io/shiftleft/controller/CustomerController.java
Original file line number Diff line number Diff line change
Expand Up @@ -277,34 +277,113 @@ public void saveSettings(HttpServletResponse httpResponse, WebRequest request) t
* @return String
* @throws IOException
*/
@RequestMapping(value = "/debug", method = RequestMethod.GET)
public String debug(@RequestParam String customerId,
@RequestParam int clientId,
@RequestParam String firstName,
@RequestParam String lastName,
@RequestParam String dateOfBirth,
@RequestParam String ssn,
@RequestParam String socialSecurityNum,
@RequestParam String tin,
@RequestParam String phoneNumber,
HttpServletResponse httpResponse,
WebRequest request) throws IOException{

// empty for now, because we debug
Set<Account> accounts1 = new HashSet<Account>();
//dateofbirth example -> "1982-01-10"
Customer customer1 = new Customer(customerId, clientId, firstName, lastName, DateTime.parse(dateOfBirth).toDate(),
ssn, socialSecurityNum, tin, phoneNumber, new Address("Debug str",
"", "Debug city", "CA", "12345"),
accounts1);

customerRepository.save(customer1);
httpResponse.setStatus(HttpStatus.CREATED.value());
httpResponse.setHeader("Location", String.format("%s/customers/%s",
request.getContextPath(), customer1.getId()));

return customer1.toString().toLowerCase().replace("script","");
}
@RequestMapping(value = "/debug", method = RequestMethod.GET, produces = MediaType.TEXT_PLAIN_VALUE)
public String debug(@RequestParam String customerId,
@RequestParam int clientId,
@RequestParam String firstName,
@RequestParam String lastName,
@RequestParam String dateOfBirth,
@RequestParam String ssn,
@RequestParam String socialSecurityNum,
@RequestParam String tin,
@RequestParam String phoneNumber,
HttpServletResponse httpResponse,
WebRequest request) throws IOException {

// Input validation for customerId
if (customerId == null || customerId.trim().isEmpty()) {
httpResponse.setStatus(HttpStatus.BAD_REQUEST.value());
return "Invalid customerId";
}

// Input validation for names
if (firstName == null || firstName.trim().isEmpty() ||
lastName == null || lastName.trim().isEmpty()) {
httpResponse.setStatus(HttpStatus.BAD_REQUEST.value());
return "Invalid name parameters";
}

// Validate date format before parsing
if (dateOfBirth == null || !dateOfBirth.matches("\\d{4}-\\d{2}-\\d{2}")) {
httpResponse.setStatus(HttpStatus.BAD_REQUEST.value());
return "Invalid date format. Expected: YYYY-MM-DD";
}

try {
// Empty account set for debug purposes
Set<Account> accounts1 = new HashSet<Account>();

// Parse date with error handling
Customer customer1 = new Customer(
customerId,
clientId,
firstName,
lastName,
DateTime.parse(dateOfBirth).toDate(),
ssn,
socialSecurityNum,
tin,
phoneNumber,
new Address("Debug str", "", "Debug city", "CA", "12345"),
accounts1
);

customerRepository.save(customer1);
httpResponse.setStatus(HttpStatus.CREATED.value());
httpResponse.setHeader("Location", String.format("%s/customers/%s",
request.getContextPath(), customer1.getId()));

// Sanitize output by HTML-encoding all user-controlled data
// This prevents XSS by converting special HTML characters to entities
String sanitizedOutput = StringEscapeUtils.escapeHtml4(customer1.toString());

// Set content type to plain text to prevent browser interpretation as HTML
httpResponse.setContentType(MediaType.TEXT_PLAIN_VALUE);

return sanitizedOutput;

} catch (IllegalArgumentException e) {
// Handle date parsing errors
httpResponse.setStatus(HttpStatus.BAD_REQUEST.value());
return "Invalid date format";
} catch (Exception e) {
// Handle other errors securely without exposing sensitive information
httpResponse.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
return "An error occurred while processing the request";
}
}


// Save customer to repository
customerRepository.save(customer1);

// Set response status and location header
httpResponse.setStatus(HttpStatus.CREATED.value());
httpResponse.setHeader("Location", String.format("%s/customers/%s",
request.getContextPath(), customer1.getId()));

// Return sanitized JSON response instead of toString() to avoid XSS
// Set content type to application/json to prevent HTML interpretation
httpResponse.setContentType("application/json");

// Create a safe JSON response with escaped values
String safeResponse = String.format(
"{\"status\":\"created\",\"customerId\":\"%s\",\"clientId\":%d,\"message\":\"Customer created successfully\"}",
StringEscapeUtils.escapeJson(customerId),
clientId
);

return safeResponse;

} catch (IllegalArgumentException e) {
// Handle date parsing errors
logger.error("Invalid date format provided: {}", dateOfBirth);
httpResponse.setStatus(HttpStatus.BAD_REQUEST.value());
httpResponse.setContentType("application/json");
return "{\"error\":\"Invalid date format. Expected format: YYYY-MM-DD\"}";
}
}


/**
* Debug test for saving and reading a customer
Expand Down
37 changes: 32 additions & 5 deletions src/main/java/io/shiftleft/model/Customer.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ public class Customer {
public Customer() {
}

public Customer(String customerId, int clientId, String firstName, String lastName, Date dateOfBirth, String ssn,
public Customer(String customerId, int clientId, String firstName, String lastName, Date dateOfBirth, String ssn,
String socialInsurancenum, String tin, String phoneNumber, Address address, Set<Account> accounts) {
super();
// Store data as-is in the model
// Sanitization should occur at the presentation layer when displaying to users
this.clientId = clientId;
this.customerId = customerId;
this.firstName = firstName;
Expand All @@ -30,7 +32,18 @@ public Customer(String customerId, int clientId, String firstName, String lastNa
this.phoneNumber = phoneNumber;
this.address = address;
this.accounts = accounts;
}
}


// Helper method to sanitize inputs
private String sanitizeInput(String input) {
if (input == null) {
return null;
}
// Remove any HTML/script tags and escape special characters
return StringEscapeUtils.escapeHtml4(input.trim());
}


@Id
@GeneratedValue(strategy = GenerationType.AUTO)
Expand Down Expand Up @@ -156,12 +169,26 @@ public void setAccounts(Set<Account> accounts) {
this.accounts = accounts;
}

@Override
public String toString() {
@Override
public String toString() {
// Note: This toString method is used for debugging purposes
// When returning this to a web response, it MUST be HTML-encoded
// The encoding is handled at the controller layer (CustomerController.debug)
return "Customer [id=" + id + ", customerId=" + customerId + ", clientId=" + clientId + ", firstName=" + firstName
+ ", lastName=" + lastName + ", dateOfBirth=" + dateOfBirth + ", ssn=" + ssn + ", socialInsurancenum="
+ socialInsurancenum + ", tin=" + tin + ", phoneNumber=" + phoneNumber + ", address=" + address + ", accounts="
+ accounts + "]";
}
}


// Helper method to mask sensitive data in logs and output
private String maskSensitiveData(String data) {
if (data == null || data.length() < 4) {
return "****";
}
// Show only last 4 characters for sensitive data
return "****" + StringEscapeUtils.escapeHtml4(data.substring(data.length() - 4));
}


}
Loading