Skip to content

Commit fd62b68

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
fix(instagram): harden OAuth and insight inputs
1 parent b706259 commit fd62b68

4 files changed

Lines changed: 40 additions & 9 deletions

File tree

apps/sim/lib/oauth/instagram.test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,16 @@ describe('instagram oauth helpers', () => {
6161
expect(
6262
parseInstagramShortLivedToken({
6363
access_token: 'short-token',
64-
user_id: 123,
64+
user_id: 17_841_467_109_118_740,
6565
permissions: ['instagram_business_basic'],
6666
})
6767
).toEqual({
6868
access_token: 'short-token',
69-
user_id: 123,
7069
permissions: ['instagram_business_basic'],
7170
})
7271
expect(
7372
parseInstagramShortLivedToken({ data: [{ access_token: 'wrapped-token', user_id: '456' }] })
74-
).toEqual({ access_token: 'wrapped-token', user_id: '456' })
73+
).toEqual({ access_token: 'wrapped-token' })
7574
})
7675

7776
it('rejects malformed or oversized token responses', () => {

apps/sim/lib/oauth/instagram.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ const instagramGraphIdSchema = z.union([
2424

2525
const instagramShortLivedTokenSchema = z.object({
2626
access_token: z.string().min(1).max(INSTAGRAM_ACCESS_TOKEN_MAX_LENGTH),
27-
user_id: instagramGraphIdSchema.optional(),
2827
permissions: z
2928
.union([
3029
z.string().min(1).max(INSTAGRAM_ACCESS_TOKEN_MAX_LENGTH),
@@ -57,7 +56,12 @@ export type InstagramShortLivedToken = z.output<typeof instagramShortLivedTokenS
5756
export type InstagramLongLivedToken = z.output<typeof instagramLongLivedTokenResponseSchema>
5857
export type InstagramProfile = z.output<typeof instagramProfileResponseSchema>
5958

60-
/** Parses the direct or legacy data-array shape returned by Instagram's code exchange. */
59+
/**
60+
* Parses the direct or legacy data-array shape returned by Instagram's code exchange.
61+
* The exchange's user_id is intentionally ignored: Meta may serialize it as a number too
62+
* large for JavaScript to represent safely, and the callback resolves the authoritative
63+
* professional-account ID from /me instead.
64+
*/
6165
export function parseInstagramShortLivedToken(value: unknown): InstagramShortLivedToken | null {
6266
const parsed = instagramShortLivedTokenResponseSchema.safeParse(value)
6367
if (!parsed.success) return null

apps/sim/tools/instagram/utils.test.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
* @vitest-environment node
33
*/
44
import { describe, expect, it } from 'vitest'
5-
import { createPublishTransform, INSTAGRAM_RESPONSE_MAX_BYTES } from '@/tools/instagram/utils'
5+
import {
6+
createPublishTransform,
7+
INSTAGRAM_RESPONSE_MAX_BYTES,
8+
parseCommaSeparated,
9+
} from '@/tools/instagram/utils'
610

711
const FALLBACK_OUTPUT = {
812
containerId: null,
@@ -84,3 +88,18 @@ describe('createPublishTransform', () => {
8488
)
8589
})
8690
})
91+
92+
describe('parseCommaSeparated', () => {
93+
it('parses nonempty comma-separated insight metrics', () => {
94+
expect(parseCommaSeparated(' reach, views,likes ')).toEqual(['reach', 'views', 'likes'])
95+
})
96+
97+
it.each([{ value: undefined }, { value: '' }, { value: ' , ' }, { value: [] }])(
98+
'rejects invalid insight metrics: $value',
99+
({ value }) => {
100+
expect(() => parseCommaSeparated(value)).toThrow(
101+
'Instagram insight metrics must be a non-empty comma-separated string'
102+
)
103+
}
104+
)
105+
})

apps/sim/tools/instagram/utils.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,12 +180,21 @@ function isCompleteInstagramPublishOutput(
180180
)
181181
}
182182

183-
export function parseCommaSeparated(value?: string): string[] {
184-
if (!value) return []
185-
return value
183+
export function parseCommaSeparated(value: unknown): string[] {
184+
if (typeof value !== 'string') {
185+
throw new Error('Instagram insight metrics must be a non-empty comma-separated string')
186+
}
187+
188+
const items = value
186189
.split(',')
187190
.map((part) => part.trim())
188191
.filter(Boolean)
192+
193+
if (items.length === 0) {
194+
throw new Error('Instagram insight metrics must be a non-empty comma-separated string')
195+
}
196+
197+
return items
189198
}
190199

191200
/** Clamp Graph pagination `limit` to a safe range (default 25, max 100). */

0 commit comments

Comments
 (0)