Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,26 @@ const PIPEDRIVE_API_KEY = 'random string'
const PIPEDRIVE_DOMAIN = 'companydomain'
const PERSON_ID = 33333

describe('Pipedrive domain validation', () => {
it('should throw when domain contains URL injection characters', async () => {
await expect(
testDestination.testAction('createUpdatePerson', {
mapping: { name: 'Test', match_value: '123' },
settings: { apiToken: PIPEDRIVE_API_KEY, domain: 'attacker.com/path?x=' }
})
).rejects.toThrowError(/Invalid domain/)
})

it('should throw when domain contains @ injection', async () => {
await expect(
testDestination.testAction('createUpdatePerson', {
mapping: { name: 'Test', match_value: '123' },
settings: { apiToken: PIPEDRIVE_API_KEY, domain: 'attacker.com@legitimate' }
})
).rejects.toThrowError(/Invalid domain/)
})
})

describe('Pipedrive.createUpdatePerson', () => {
it('should create person if none exists', async () => {
const scope = nock(`https://${PIPEDRIVE_DOMAIN}.pipedrive.com/api/v1`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import createUpdateOrganization from './createUpdateOrganization'
import createUpdatePerson from './createUpdatePerson'
import { defaultValues, DestinationDefinition } from '@segment/actions-core'
import type { Settings } from './generated-types'
import { validateDomain } from './utils'

import createUpdateActivity from './createUpdateActivity'

Expand Down Expand Up @@ -59,6 +60,7 @@ const destination: DestinationDefinition<Settings> = {
}
},
testAuthentication: (request, { settings }) => {
validateDomain(settings.domain)
return request(`https://${settings.domain}.pipedrive.com/api/v1/users/me`)
}
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Settings } from '../generated-types'
import type { ExecuteInput, ModifiedResponse, RequestClient } from '@segment/actions-core'
import { validateDomain } from '../utils'
import get from 'lodash/get'
import { ActivityTypes, PipedriveFields } from './domain'
import { DynamicFieldResponse } from '@segment/actions-core'
Expand Down Expand Up @@ -52,6 +53,7 @@ class PipedriveClient {
private _request: RequestClient

constructor(settings: Settings, request: RequestClient) {
validateDomain(settings.domain)
Comment thread
AnkitSegment marked this conversation as resolved.
Outdated
this.settings = settings
this._request = request
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { InvalidAuthenticationError } from '@segment/actions-core'

type PayloadWithCustomFields = { custom_fields?: { [k: string]: unknown } }

export function validateDomain(domain: string): void {
if (!/^[a-zA-Z0-9-]+$/.test(domain)) {
throw new InvalidAuthenticationError(
'Invalid domain. Domain must contain only alphanumeric characters and hyphens.'
)
}
}
Comment thread
AnkitSegment marked this conversation as resolved.

export function addCustomFieldsFromPayloadToEntity<E extends object>(payload: PayloadWithCustomFields, entity: E) {
if (!payload.custom_fields) {
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,23 @@ describe('Qualtrics', () => {

await expect(testDestination.testAuthentication(authData)).rejects.toThrowError(/401/)
})

it('throw error when datacenter contains URL injection characters', async () => {
const authData = {
apiToken: 'VALID_API_TOKEN_VALUE',
datacenter: 'attacker.com/path?x='
}

await expect(testDestination.testAuthentication(authData)).rejects.toThrowError(/Invalid datacenter ID/)
})

it('throw error when datacenter contains @ injection', async () => {
const authData = {
apiToken: 'VALID_API_TOKEN_VALUE',
datacenter: 'attacker.com@legitimate'
}

await expect(testDestination.testAuthentication(authData)).rejects.toThrowError(/Invalid datacenter ID/)
})
})
})
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RequestClient } from '@segment/actions-core'
import { RequestClient, InvalidAuthenticationError } from '@segment/actions-core'

export type SupportedMethods = 'get' | 'post'

Expand Down Expand Up @@ -125,7 +125,13 @@ export default class QualtricsApiClient {
private request: RequestClient

constructor(dc: string, apiToken: string, request: RequestClient) {
this.baseUrl = `https://${dc || 'iad1'}.qualtrics.com`
const datacenter = dc || 'iad1'
if (!/^[a-zA-Z0-9-]+$/.test(datacenter)) {
throw new InvalidAuthenticationError(
'Invalid datacenter ID. Datacenter must contain only alphanumeric characters and hyphens.'
)
}
this.baseUrl = `https://${datacenter}.qualtrics.com`
this.apiToken = apiToken
this.request = request
}
Comment on lines 127 to 132
Expand Down
Loading