Skip to content

Commit 893d086

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
fix(oracle-epm): reject unusable link policies
1 parent 5e32a64 commit 893d086

4 files changed

Lines changed: 69 additions & 2 deletions

File tree

apps/sim/lib/internal/oracle-epm/links.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,26 @@ describe('Oracle EPM returned-link declarations', () => {
5757
'not a valid declaration'
5858
)
5959
})
60+
61+
it('rejects endpoint-bound policies whose required headers cannot be supplied', () => {
62+
const endpoint = routes.defineEndpoint({
63+
method: 'GET',
64+
version: 'v3',
65+
path: [oracleEpmLiteral('download')],
66+
headers: { range: { name: 'Range', required: true, maxBytes: 64 } },
67+
body: 'none',
68+
response: 'stream',
69+
timeoutMs: 2_000,
70+
maxResponseBytes: 1_024,
71+
})
72+
73+
expect(() =>
74+
routes.defineReturnedLinkPolicy({
75+
relation: 'download',
76+
method: 'GET',
77+
endpoint,
78+
preserveGatewayBasePath: true,
79+
})
80+
).toThrow('input contract')
81+
})
6082
})

apps/sim/lib/internal/oracle-epm/links.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,12 @@ export function defineOracleEpmReturnedLinkPolicy(
5757
if (
5858
endpoint.routeSpace !== routeSpace ||
5959
endpoint.method !== declaration.method ||
60-
endpoint.body !== 'none'
60+
endpoint.body !== 'none' ||
61+
Object.values(endpoint.headers ?? {}).some((header) => header.required)
6162
) {
62-
throw new Error('Oracle EPM returned-link policy endpoint does not match its route or method')
63+
throw new Error(
64+
'Oracle EPM returned-link policy endpoint does not match its route, method, or input contract'
65+
)
6366
}
6467
endpointDefinition = endpoint
6568
version = endpoint.version

apps/sim/lib/oauth/token-resolution.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,33 @@ describe('resolveCredentialAccessToken', () => {
538538
)
539539
})
540540

541+
it('fails closed when declared tool service metadata has no registered service', async () => {
542+
mockResolveOAuthAccountId.mockResolvedValue({
543+
accountId: 'credential-1',
544+
credentialId: 'credential-1',
545+
credentialType: 'service_account',
546+
providerId: 'oracle-epm-service-account',
547+
usedCredentialTable: true,
548+
})
549+
mockGetServiceConfigByProviderId.mockReturnValue(null)
550+
551+
await expect(
552+
resolveCredentialAccessToken({
553+
requestId: 'req-1',
554+
credentialId: 'credential-1',
555+
toolId: 'synthetic_oracle_tool',
556+
authenticate,
557+
})
558+
).resolves.toEqual({
559+
ok: false,
560+
status: 403,
561+
code: 'CREDENTIAL_PROVIDER_MISMATCH',
562+
error: 'Credential does not match the tool service',
563+
})
564+
expect(authenticate).not.toHaveBeenCalled()
565+
expect(mockResolveServiceAccountToken).not.toHaveBeenCalled()
566+
})
567+
541568
it('rejects a mismatched OAuth account after loading its authoritative provider', async () => {
542569
mockResolveOAuthAccountId.mockResolvedValue({
543570
accountId: 'account-1',

apps/sim/lib/oauth/token-resolution.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,21 @@ export async function resolveCredentialAccessToken(
379379
? getServiceConfigByProviderId(toolMetadata.oauth.provider)
380380
: null
381381

382+
if (
383+
credentialId &&
384+
toolId &&
385+
resolved?.credentialType !== 'managed_oauth' &&
386+
toolMetadata?.oauth?.required &&
387+
!expectedService
388+
) {
389+
return {
390+
ok: false,
391+
status: 403,
392+
code: 'CREDENTIAL_PROVIDER_MISMATCH',
393+
error: 'Credential does not match the tool service',
394+
}
395+
}
396+
382397
if (
383398
resolved?.credentialType !== 'managed_oauth' &&
384399
resolved?.providerId &&

0 commit comments

Comments
 (0)