-
Notifications
You must be signed in to change notification settings - Fork 0
feat: guest screening risk assessment #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reviewed the new vacation rental guest screening feature for LLM security vulnerabilities. The code sends highly sensitive guest PII (SSN, credit reports, bank account numbers, criminal records) directly to a cloud LLM service without data minimization, creating critical privacy and regulatory risks. Additionally, there's a missing authorization check that could allow unauthorized access to guest screening data.
Minimum severity threshold for this scan: Medium
| GUEST APPLICATION DATA: | ||
| ${JSON.stringify(guestApplication, null, 2)} | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔴 Critical
The entire guest application object is serialized and sent to the cloud LLM, including highly sensitive PII like Social Security Numbers, bank account numbers, credit reports, and criminal records. This data is unnecessary for the risk assessment criteria defined in your prompt (lines 40-113) and creates significant privacy, regulatory, and security exposure.
💡 Suggested Fix
Implement data minimization by creating a sanitized subset of guest data before sending to the LLM:
function sanitizeGuestDataForAssessment(
guestApplication: GuestApplication
): Record<string, any> {
return {
// Only include fields needed for risk assessment
verified: guestApplication.verified,
verifiedId: guestApplication.verifiedId,
memberSince: guestApplication.memberSince,
totalReviews: guestApplication.totalReviews,
averageRating: guestApplication.averageRating,
previousBookings: guestApplication.previousBookings,
cancellationRate: guestApplication.cancellationRate,
checkIn: guestApplication.checkIn,
checkOut: guestApplication.checkOut,
numberOfGuests: guestApplication.numberOfGuests,
bookingPurpose: guestApplication.bookingPurpose,
guestMessage: guestApplication.guestMessage,
backgroundCheckStatus: guestApplication.backgroundCheckStatus,
hasCleanBackground: guestApplication.criminalRecords.length === 0,
creditScoreRange: getCreditScoreRange(guestApplication.creditScore),
reviews: guestApplication.reviews,
};
}
function getCreditScoreRange(score: number): string {
if (score >= 740) return 'excellent';
if (score >= 670) return 'good';
if (score >= 580) return 'fair';
return 'poor';
}
// Then update line 93:
GUEST APPLICATION DATA:
${JSON.stringify(sanitizeGuestDataForAssessment(guestApplication), null, 2)}This removes SSN, bank accounts, driver's license numbers, ID images, detailed credit reports, addresses, emergency contacts, and other sensitive fields while preserving all data needed for the risk assessment criteria.
🤖 AI Agent Prompt
The code at src/routes/rental.ts:92-94 sends the entire guest application object (including SSN, bank account numbers, credit reports, criminal records, biometric ID images, and other highly sensitive PII) to a cloud LLM service. This violates data minimization principles and creates regulatory risks (GDPR, CCPA, FCRA, biometric privacy laws).
Investigate the GuestApplication type definition in src/types/rental.ts to understand all fields being sent. Compare this against the evaluation criteria defined in lines 40-113 of the same file to identify which fields are actually necessary for risk assessment.
Create a sanitization function that extracts only the minimum necessary fields: verification status, booking history metrics (reviews, ratings, cancellation rate), current booking details, and high-level background/credit indicators (status summaries, not detailed records). Replace the raw SSN, bank account numbers, and detailed criminal records with boolean flags or categorical ranges where possible.
The goal is to preserve full risk assessment functionality while removing all fields that provide no value for the stated evaluation criteria. Test that the LLM can still perform effective risk assessments with the reduced dataset.
| router.post('/authorized/:level/rental/send-message', async (req: Request, res: Response) => { | ||
| try { | ||
| const pathParams = pathParamsSchema.parse(req.params); | ||
| const { level } = pathParams; | ||
|
|
||
| const bodyParams = sendMessageBodySchema.parse(req.body); | ||
| const { applicationId, message, propertyOwnerEmail, model } = bodyParams; | ||
|
|
||
| const database = loadGuestApplications(); | ||
| const guestApplication = database.applications.find( | ||
| (app) => app.applicationId === applicationId | ||
| ); | ||
|
|
||
| if (!guestApplication) { | ||
| return res.status(404).json({ | ||
| error: 'Application not found', | ||
| message: `No guest application found with ID: ${applicationId}`, | ||
| }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Medium
The /rental/send-message endpoint accepts an applicationId and propertyOwnerEmail from the request but doesn't verify that the authenticated user owns the property associated with that application. This could allow an authenticated attacker to access guest screening data for properties they don't own and send emails to arbitrary recipients.
💡 Suggested Fix
Add property ownership validation before processing the request:
// Add authorization helper function
async function validatePropertyOwnership(
authenticatedUserId: string,
applicationId: string,
database: GuestApplicationDatabase
): Promise<{ authorized: boolean; application?: GuestApplication }> {
const application = database.applications.find(
(app) => app.applicationId === applicationId
);
if (!application) {
return { authorized: false };
}
// Verify user owns the property (integrate with your auth system)
const ownsProperty = await checkUserOwnsProperty(
authenticatedUserId,
application.propertyId
);
return {
authorized: ownsProperty,
application: ownsProperty ? application : undefined,
};
}
// Update the endpoint to check authorization:
router.post('/authorized/:level/rental/send-message', async (req: Request, res: Response) => {
// ... existing param parsing ...
const database = loadGuestApplications();
const authenticatedUserId = req.user?.id; // Adapt to your auth system
const { authorized, application: guestApplication } = await validatePropertyOwnership(
authenticatedUserId,
applicationId,
database
);
if (!authorized || !guestApplication) {
return res.status(403).json({
error: 'Forbidden',
message: 'You do not have permission to access this guest application',
});
}
// Continue with existing logic...
});Apply the same authorization check to the /rental/screen-guest endpoint (lines 185-227).
🤖 AI Agent Prompt
The /rental/send-message endpoint at src/routes/rental.ts:229-247 accepts applicationId and propertyOwnerEmail from the request without verifying that the authenticated user has permission to access that guest application. This creates an authorization gap where any authenticated user could potentially access screening data for properties they don't own.
Investigate how property ownership is tracked in the codebase. Look for existing authorization patterns that verify property ownership (search for similar checks in other endpoints, or check if there's a properties database/table that links properties to owners). The GuestApplication object contains a propertyId field that should be used to look up the property owner.
Implement an authorization check that:
- Retrieves the application by
applicationId - Extracts the
propertyIdfrom the application - Verifies that the authenticated user (from
req.useror your auth middleware) owns or has access to that property - Returns a 403 Forbidden error if unauthorized
Also check if the propertyOwnerEmail should be validated against the property's registered owner email to prevent sending notifications to arbitrary addresses. Apply the same authorization pattern to both the /rental/send-message and /rental/screen-guest endpoints.
Adds an LLM-generated risk assessment to the vacation rental app. This helps owners screen potential guests more easily. It's included in email message to owner when a guest make an inquiry about a rental property to provide helpful context to owner.