Fix SQL injection, wrong PK, and hardcoded production URLs

Security:
- orders/submit.cfm: parameterize IN clause (was string-interpolated)
- auth/completeProfile.cfm: fix UserID → ID on Users table PK

Environment-aware URLs:
- Add application.baseUrl to config/environment.cfm
- Replace all hardcoded https://biz.payfrit.com with application.baseUrl in:
  orders/getDetail, tasks/getDetails, auth/completeProfile, auth/avatar,
  stripe/onboard, users/search, workers/onboardingLink, workers/earlyUnlock

Also fix submit.cfm qMeta.ItemID → qMeta.ID (column not in SELECT)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
John Mizerek 2026-01-31 21:14:19 -08:00
parent e30a757c39
commit 3f15b0c8b6
10 changed files with 15 additions and 14 deletions

View file

@ -88,7 +88,7 @@ function findAvatarFile(basePath, userId) {
avatarInfo = findAvatarFile(uploadsPath, userId);
avatarPath = avatarInfo.path;
avatarFilename = avatarInfo.filename;
avatarUrl = "https://biz.payfrit.com/uploads/users/" & avatarFilename;
avatarUrl = application.baseUrl & "/uploads/users/" & avatarFilename;
// Handle GET - return current avatar URL
if (cgi.REQUEST_METHOD == "GET") {

View file

@ -74,7 +74,7 @@ try {
SELECT ID FROM Users
WHERE EmailAddress = :email
AND IsEmailVerified = 1
AND UserID != :userId
AND ID != :userId
LIMIT 1
", {
email: { value: email, cfsqltype: "cf_sql_varchar" },
@ -109,7 +109,7 @@ try {
}, { datasource: "payfrit" });
// Send confirmation email (non-blocking - don't fail if mail fails)
confirmLink = "https://biz.payfrit.com/confirm_email.cfm?UUID=" & qUser.UUID;
confirmLink = application.baseUrl & "/confirm_email.cfm?UUID=" & qUser.UUID;
emailBody = "
<p>Welcome to Payfrit, #firstName#!</p>
<p>Please click the link below to confirm your email address:</p>

View file

@ -258,6 +258,7 @@ function logPerf(numeric responseBytes = 0) {
// Store in application scope
application.isDevEnvironment = isDevEnvironment;
application.baseUrl = isDevEnvironment ? "https://dev.payfrit.com" : "https://biz.payfrit.com";
application.isDev = isDev;
application.logDebug = logDebug;
application.apiError = apiError;

View file

@ -174,7 +174,7 @@ try {
staffMember = structNew("ordered");
staffMember["UserID"] = row.ID;
staffMember["FirstName"] = row.FirstName;
staffMember["AvatarUrl"] = "https://biz.payfrit.com/uploads/users/" & row.ID & ".jpg";
staffMember["AvatarUrl"] = application.baseUrl & "/uploads/users/" & row.ID & ".jpg";
staffMember["RatingToken"] = row.RatingToken ?: "";
arrayAppend(staff, staffMember);
}

View file

@ -83,14 +83,14 @@
RequiresChildSelection,
MaxNumSelectionReq
FROM Items
WHERE ID IN (#inList#)
WHERE ID IN (:itemIds)
",
[],
{ itemIds: { value: inList, cfsqltype: "cf_sql_integer", list: true } },
{ datasource = "payfrit" }
)>
<cfloop query="qMeta">
<cfset out.itemMeta[qMeta.ItemID] = {
<cfset out.itemMeta[qMeta.ID] = {
"requires": qMeta.RequiresChildSelection,
"maxSel": qMeta.MaxNumSelectionReq
}>

View file

@ -83,7 +83,7 @@ try {
}
// Create account link for onboarding
baseURL = "https://biz.payfrit.com";
baseURL = application.baseUrl;
httpService = new http();
httpService.setMethod("POST");

View file

@ -86,11 +86,11 @@
<cfset pngPath = uploadDir & qTask.CustomerUserID & ".png">
<cfset pngPathUpper = uploadDir & qTask.CustomerUserID & ".PNG">
<cfif fileExists(jpgPath)>
<cfset customerPhotoUrl = "https://biz.payfrit.com/uploads/users/" & qTask.CustomerUserID & ".jpg">
<cfset customerPhotoUrl = application.baseUrl & "/uploads/users/" & qTask.CustomerUserID & ".jpg">
<cfelseif fileExists(pngPath)>
<cfset customerPhotoUrl = "https://biz.payfrit.com/uploads/users/" & qTask.CustomerUserID & ".png">
<cfset customerPhotoUrl = application.baseUrl & "/uploads/users/" & qTask.CustomerUserID & ".png">
<cfelseif fileExists(pngPathUpper)>
<cfset customerPhotoUrl = "https://biz.payfrit.com/uploads/users/" & qTask.CustomerUserID & ".PNG">
<cfset customerPhotoUrl = application.baseUrl & "/uploads/users/" & qTask.CustomerUserID & ".PNG">
</cfif>
</cfif>

View file

@ -73,7 +73,7 @@ try {
"Name": trim(user.FirstName & " " & user.LastName),
"Email": user.EmailAddress,
"Phone": maskedPhone,
"AvatarUrl": len(trim(user.ImageExtension)) ? "https://biz.payfrit.com/uploads/users/" & user.ID & "." & user.ImageExtension : ""
"AvatarUrl": len(trim(user.ImageExtension)) ? application.baseUrl & "/uploads/users/" & user.ID & "." & user.ImageExtension : ""
});
}

View file

@ -59,7 +59,7 @@ try {
httpService.addParam(type="formfield", name="line_items[0][price_data][currency]", value="usd");
httpService.addParam(type="formfield", name="line_items[0][price_data][product_data][name]", value="Payfrit Activation");
httpService.addParam(type="formfield", name="line_items[0][quantity]", value="1");
baseUrl = application.isDevEnvironment ? "https://dev.payfrit.com" : "https://biz.payfrit.com";
baseUrl = application.baseUrl;
httpService.addParam(type="formfield", name="success_url", value=baseUrl & "/works/stripe-return.cfm?status=success");
httpService.addParam(type="formfield", name="cancel_url", value=baseUrl & "/works/stripe-return.cfm?status=cancel");
httpService.addParam(type="formfield", name="metadata[user_id]", value=userID);

View file

@ -53,7 +53,7 @@ try {
httpService.setPassword("");
httpService.addParam(type="formfield", name="account", value=accountID);
baseUrl = application.isDevEnvironment ? "https://dev.payfrit.com" : "https://biz.payfrit.com";
baseUrl = application.baseUrl;
httpService.addParam(type="formfield", name="refresh_url", value=baseUrl & "/works/stripe-return.cfm?status=refresh");
httpService.addParam(type="formfield", name="return_url", value=baseUrl & "/works/stripe-return.cfm?status=complete");
httpService.addParam(type="formfield", name="type", value="account_onboarding");