-
-
Notifications
You must be signed in to change notification settings - Fork 288
Make address option for kyc verification, add check to payee #11253
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
server/lib/security/expense.ts
Outdated
| if (payeeCollective?.type === CollectiveType.USER) { | ||
| const kycVerifications = await Promise.all( | ||
| Object.values(KYCProviderName).map(provider => | ||
| req.loaders.KYCVerification.verifiedStatusByProvider(expense.HostCollectiveId, provider).load( |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
kewitz
left a comment
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.
Please double-check relying on expense.HostCollectiveId for security checks on pending expenses.
| data: { | ||
| legalAddress: providerRequest.legalAddress, | ||
| legalName: providerRequest.legalName, | ||
| ...(providerRequest.legalAddress ? { legalAddress: providerRequest.legalAddress } : {}), |
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.
As a suggestion, Object.assign accepts false arguments:
data: Object.assign({ legalName: providerRequest.legalName }, providerRequest.legalAddress && { legalAddress: providerRequest.legalAddress })In this particular situation pick(providerRequest, ['legalName', 'legalAddress']) would also have the same effect.
server/lib/security/expense.ts
Outdated
| if (payeeCollective?.type === CollectiveType.USER) { | ||
| const kycVerifications = await Promise.all( | ||
| Object.values(KYCProviderName).map(provider => | ||
| req.loaders.KYCVerification.verifiedStatusByProvider(expense.HostCollectiveId, provider).load( |
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'm not sure how the code stands now, but please review exactly in which conditions expense.HostCollectiveId is populated. It used to be that it was populated only after the expense was paid, to cover for cases where the collective would change hosts with existing pending expenses.
server/lib/security/expense.ts
Outdated
| if (payeeCollective?.type === CollectiveType.USER) { | ||
| const kycVerifications = await Promise.all( | ||
| Object.values(KYCProviderName).map(provider => | ||
| req.loaders.KYCVerification.verifiedStatusByProvider(expense.HostCollectiveId, provider).load( |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| const hostCollectiveId = expense.HostCollectiveId || expense.collective?.HostCollectiveId; | ||
|
|
||
| // KYC Verification Check: only for individual payees | ||
| const payeeCollective = | ||
| expense.fromCollective || (await req.loaders.Collective.byId.load(expense.FromCollectiveId)); | ||
| if (payeeCollective?.type === CollectiveType.USER) { | ||
| const kycVerifications = await Promise.all( | ||
| Object.values(KYCProviderName).map(provider => | ||
| req.loaders.KYCVerification.verifiedStatusByProvider(hostCollectiveId, provider).load(payeeCollective.id), |
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.
Bug: hostCollectiveId can be undefined if an expense has no host. This causes the KYC verification loader to receive undefined and silently fail, bypassing the check.
Severity: CRITICAL | Confidence: High
🔍 Detailed Analysis
The hostCollectiveId variable can become undefined if both expense.HostCollectiveId and expense.collective?.HostCollectiveId are null. This undefined value is then passed to req.loaders.KYCVerification.verifiedStatusByProvider. The underlying SQL query will use NULL for the RequestedByCollectiveId, which will not match any records because NULL = NULL is not true in SQL. As a result, the kycVerifications array will contain only null values, and the subsequent loop will silently skip all KYC checks, potentially allowing payments to unverified individuals.
💡 Suggested Fix
Add a check to ensure hostCollectiveId is a valid number before calling req.loaders.KYCVerification.verifiedStatusByProvider. If hostCollectiveId is not available, either throw an error or handle the case explicitly to prevent the silent failure of the KYC check.
🤖 Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: server/lib/security/expense.ts#L506-L514
Potential issue: The `hostCollectiveId` variable can become `undefined` if both
`expense.HostCollectiveId` and `expense.collective?.HostCollectiveId` are null. This
`undefined` value is then passed to
`req.loaders.KYCVerification.verifiedStatusByProvider`. The underlying SQL query will
use `NULL` for the `RequestedByCollectiveId`, which will not match any records because
`NULL = NULL` is not true in SQL. As a result, the `kycVerifications` array will contain
only `null` values, and the subsequent loop will silently skip all KYC checks,
potentially allowing payments to unverified individuals.
Did we get this right? 👍 / 👎 to inform future reviews.
Reference ID: 8181271
Related opencollective/opencollective#8341
Resolves opencollective/opencollective#8369