This repository has been archived on 2026-03-21. You can view files and clone it, but cannot push or open issues or pull requests.
payfrit-biz/api/users/search.cfm
John Mizerek 3f15b0c8b6 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>
2026-01-31 21:14:19 -08:00

93 lines
2.7 KiB
Text

<cfsetting showdebugoutput="false">
<cfsetting enablecfoutputonly="true">
<cfcontent type="application/json; charset=utf-8" reset="true">
<cfscript>
// Search users by phone, email, or username
// Input: Query (search term)
// Output: { OK: true, USERS: [...] }
function apiAbort(required struct payload) {
writeOutput(serializeJSON(payload));
abort;
}
function readJsonBody() {
var raw = getHttpRequestData().content;
if (isNull(raw)) raw = "";
if (!len(trim(raw))) return {};
try {
var data = deserializeJSON(raw);
if (isStruct(data)) return data;
} catch (any e) {}
return {};
}
try {
data = readJsonBody();
query = structKeyExists(data, "Query") ? trim(data.Query) : "";
currentUserId = val(structKeyExists(data, "CurrentUserID") ? data.CurrentUserID : 0);
if (len(query) < 3) {
apiAbort({ "OK": true, "USERS": [], "MESSAGE": "Query must be at least 3 characters" });
}
// Search by phone, email, or first/last name
// Exclude the current user from results
searchTerm = "%" & query & "%";
qUsers = queryExecute("
SELECT
u.ID,
u.FirstName,
u.LastName,
u.EmailAddress,
u.ContactNumber,
u.ImageExtension
FROM Users u
WHERE u.ID != :currentUserId
AND (
u.ContactNumber LIKE :searchTerm
OR u.EmailAddress LIKE :searchTerm
OR u.FirstName LIKE :searchTerm
OR u.LastName LIKE :searchTerm
OR CONCAT(u.FirstName, ' ', u.LastName) LIKE :searchTerm
)
ORDER BY u.FirstName, u.LastName
LIMIT 10
", {
currentUserId: { value: currentUserId, cfsqltype: "cf_sql_integer" },
searchTerm: { value: searchTerm, cfsqltype: "cf_sql_varchar" }
}, { datasource: "payfrit" });
users = [];
for (user in qUsers) {
// Mask phone number for privacy (show last 4 digits only)
maskedPhone = "";
if (len(trim(user.ContactNumber)) >= 4) {
maskedPhone = "***-***-" & right(user.ContactNumber, 4);
}
arrayAppend(users, {
"UserID": user.ID,
"Name": trim(user.FirstName & " " & user.LastName),
"Email": user.EmailAddress,
"Phone": maskedPhone,
"AvatarUrl": len(trim(user.ImageExtension)) ? application.baseUrl & "/uploads/users/" & user.ID & "." & user.ImageExtension : ""
});
}
apiAbort({
"OK": true,
"USERS": users,
"COUNT": arrayLen(users)
});
} catch (any e) {
apiAbort({
"OK": false,
"ERROR": "server_error",
"MESSAGE": e.message
});
}
</cfscript>