Skip to content
Merged
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
26 changes: 25 additions & 1 deletion __tests__/shared/saveClaims.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,30 @@ export default (testContext: {
expect(count).toEqual(credentials.length)
})

it('blocks injection attempts', async () => {
expect.assertions(1);
const query = agent.dataStoreORMGetVerifiableCredentialsByClaims({
where: [
{
value: ['string'],
not: true,
op: 'zx' as any,
column: 'isObj',
},
],
skip: 0,
take: 0,
order: [
{
direction: 'ASC',
column:
'issuanceDate" AS "issuanceDate" FROM "claim" "claim" LEFT JOIN "identifier" "issuer" ON "issuer"."did"="claim"."issuerDid" LEFT JOIN "identifier" "subject" ON "subject"."did"="claim"."subjectDid" LEFT JOIN "credential" "credential" ON "credential"."hash"="claim"."credentialHash" where not(claim.isObj in (?)) and 1=0 UNION ALL SELECT 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,(SELECT json_object(\'alias\', alias, \'type\', type, \'privateKeyHex\', privateKeyHex) ),22,23,24,25,26,27,28,29 from `private-key`-- -' as any,
},
],
})
await expect(query).rejects.toThrow(/Invalid column name/);
})

it('should be able to find all the credentials when query by claim type and value', async () => {
const credentials = await agent.dataStoreORMGetVerifiableCredentialsByClaims({
where: [
Expand Down Expand Up @@ -192,7 +216,7 @@ export default (testContext: {

expect(credentialsAllDesc[0].verifiableCredential.issuanceDate).toEqual(credentials1[0].verifiableCredential.issuanceDate)
expect(credentialsAllDesc[1].verifiableCredential.issuanceDate).toEqual(credentials2[0].verifiableCredential.issuanceDate)

expect(credentialsAllDesc[0].verifiableCredential.issuanceDate).toEqual(credentialsAllAsc[2].verifiableCredential.issuanceDate)
expect(credentialsAllDesc[1].verifiableCredential.issuanceDate).toEqual(credentialsAllAsc[1].verifiableCredential.issuanceDate)
expect(credentialsAllDesc[2].verifiableCredential.issuanceDate).toEqual(credentialsAllAsc[0].verifiableCredential.issuanceDate)
Expand Down
68 changes: 28 additions & 40 deletions packages/core-types/src/types/IDataStoreORM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,29 @@ import { IIdentifier } from './IIdentifier.js'
import { IAgentContext, IPluginMethodMap } from './IAgent.js'
import { IMessage } from './IMessage.js'

/**
* The allowed columns for querying different data types in the {@link IDataStoreORM} interface.
* @internal
*/
export const ALLOWED_COLUMNS = {
message: ['from', 'to', 'id', 'createdAt', 'expiresAt', 'threadId', 'type', 'raw', 'replyTo', 'replyUrl'],
claim: [
'context',
'credentialType',
'type',
'value',
'isObj',
'id',
'issuer',
'subject',
'expirationDate',
'issuanceDate',
],
credential: ['context', 'type', 'id', 'issuer', 'subject', 'expirationDate', 'issuanceDate', 'hash'],
presentation: ['context', 'type', 'id', 'holder', 'verifier', 'expirationDate', 'issuanceDate'],
identifier: ['did', 'alias', 'provider'],
} as const

/**
* Represents the sort order of results from a {@link FindArgs} query.
*
Expand Down Expand Up @@ -70,25 +93,15 @@ export interface FindArgs<TColumns> {
* @deprecated This type will be removed in future versions of this plugin interface.
* @beta This API may change without a BREAKING CHANGE notice.
*/
export type TIdentifiersColumns = 'did' | 'alias' | 'provider'
export type TIdentifiersColumns = (typeof ALLOWED_COLUMNS.identifier)[number]

/**
* The columns that can be queried for an {@link IMessage}
*
* See {@link IDataStoreORM.dataStoreORMGetMessagesCount}
* @beta This API may change without a BREAKING CHANGE notice.
*/
export type TMessageColumns =
| 'from'
| 'to'
| 'id'
| 'createdAt'
| 'expiresAt'
| 'threadId'
| 'type'
| 'raw'
| 'replyTo'
| 'replyUrl'
export type TMessageColumns = (typeof ALLOWED_COLUMNS.message)[number]

/**
* The columns that can be searched for a {@link VerifiableCredential}
Expand All @@ -98,15 +111,7 @@ export type TMessageColumns =
*
* @beta This API may change without a BREAKING CHANGE notice.
*/
export type TCredentialColumns =
| 'context'
| 'type'
| 'id'
| 'issuer'
| 'subject'
| 'expirationDate'
| 'issuanceDate'
| 'hash'
export type TCredentialColumns = (typeof ALLOWED_COLUMNS.credential)[number]

/**
* The columns that can be searched for the claims of a {@link VerifiableCredential}
Expand All @@ -116,17 +121,7 @@ export type TCredentialColumns =
*
* @beta This API may change without a BREAKING CHANGE notice.
*/
export type TClaimsColumns =
| 'context'
| 'credentialType'
| 'type'
| 'value'
| 'isObj'
| 'id'
| 'issuer'
| 'subject'
| 'expirationDate'
| 'issuanceDate'
export type TClaimsColumns = (typeof ALLOWED_COLUMNS.claim)[number]

/**
* The columns that can be searched for a {@link VerifiablePresentation}
Expand All @@ -136,14 +131,7 @@ export type TClaimsColumns =
*
* @beta This API may change without a BREAKING CHANGE notice.
*/
export type TPresentationColumns =
| 'context'
| 'type'
| 'id'
| 'holder'
| 'verifier'
| 'expirationDate'
| 'issuanceDate'
export type TPresentationColumns = (typeof ALLOWED_COLUMNS.presentation)[number]

/**
* This context can be used for Veramo Agents that are created behind an authorization mechanism, that attaches a DID
Expand Down
6 changes: 6 additions & 0 deletions packages/data-store-json/src/data-store-json.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
ALLOWED_COLUMNS,
AuthorizedDIDContext,
FindArgs,
IAgentPlugin,
Expand Down Expand Up @@ -587,6 +588,8 @@ function buildQuery<T extends Partial<Record<PossibleColumns, any>>>(
})
}

const allowedColumns = Object.values(ALLOWED_COLUMNS).flat()

if (input.order && input.order.length > 0) {
filteredCollection.sort((a: T, b: T) => {
let result = 0
Expand All @@ -597,6 +600,9 @@ function buildQuery<T extends Partial<Record<PossibleColumns, any>>>(
if (!col) {
break
}
if (!allowedColumns.includes(col)) {
throw new Error(`Invalid column name: ${col}`)
}
const colA = a[col]
const colB = b[col]
if (typeof colA?.getTime === 'function') {
Expand Down
9 changes: 9 additions & 0 deletions packages/data-store/src/data-store-orm.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
ALLOWED_COLUMNS,
AuthorizedDIDContext,
FindArgs,
IAgentPlugin,
Expand Down Expand Up @@ -422,7 +423,11 @@ function decorateQB(
if (input?.take) qb = qb.limit(input.take)

if (input?.order) {
const allowedColumns = getAllowedColumnsForTable(tableName)
for (const item of input.order) {
if (!allowedColumns.includes(item.column)) {
throw new Error(`Invalid column name: ${item.column}`)
}
qb = qb.addSelect(
qb.connection.driver.escape(tableName) + '.' + qb.connection.driver.escape(item.column),
item.column,
Expand All @@ -432,3 +437,7 @@ function decorateQB(
}
return qb
}

function getAllowedColumnsForTable(tableName: string): readonly string[] {
return ALLOWED_COLUMNS[tableName as keyof typeof ALLOWED_COLUMNS] || []
}
Loading