Skip to content

Commit 9e738a3

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
fix(oracle-epm): support slash-containing link relations
1 parent 9ec1be0 commit 9e738a3

3 files changed

Lines changed: 69 additions & 39 deletions

File tree

apps/sim/lib/internal/oracle-epm/client.server.test.ts

Lines changed: 59 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,7 @@ describe('Oracle EPM guarded client', () => {
550550
}
551551
)
552552

553-
it.each(['download', 'Job Status'])(
553+
it.each(['download', 'Job Status', 'exported/artifact', 'imported/artifact'])(
554554
'keeps %s links opaque and client-owned',
555555
async (relation) => {
556556
const download = routes.defineEndpoint({
@@ -626,7 +626,12 @@ describe('Oracle EPM guarded client', () => {
626626
'https://epm.example.com/gateway/SyntheticAlpha/rest/v3/files%2Fabc?token=x',
627627
'https://epm.example.com/gateway/SyntheticAlpha/rest/v3/files%5Cabc?token=x',
628628
'https://epm.example.com/gateway/SyntheticAlpha/rest/v3/files\\abc?token=x',
629-
].flatMap((href) => ['download', 'Job Status'].map((relation) => ({ relation, href })))
629+
].flatMap((href) =>
630+
['download', 'Job Status', 'exported/artifact', 'imported/artifact'].map((relation) => ({
631+
relation,
632+
href,
633+
}))
634+
)
630635
)('rejects unsafe $relation link $href', ({ relation, href }) => {
631636
const policy = routes.defineReturnedLinkPolicy({
632637
relation,
@@ -650,44 +655,62 @@ describe('Oracle EPM guarded client', () => {
650655
expect(mockSecureFetch).not.toHaveBeenCalled()
651656
})
652657

653-
it.each(['download', 'Job Status'])('rejects an incorrect %s link method', (relation) => {
654-
const policy = routes.defineReturnedLinkPolicy({
655-
relation,
656-
method: 'GET',
657-
version: 'v3',
658-
path: [oracleEpmLiteral('files'), oracleEpmPathParameter('fileId', { maxBytes: 32 })],
659-
response: 'stream',
660-
timeoutMs: 5_000,
661-
maxResponseBytes: 4_096,
662-
preserveGatewayBasePath: true,
663-
})
664-
const client = createOracleEpmClient({
665-
instanceUrl: 'https://epm.example.com/gateway',
666-
accessToken: Buffer.from('u:p').toString('base64'),
667-
})
668-
expect(() =>
669-
client.validateReturnedLink(policy, {
670-
rel: relation,
671-
method: 'POST',
672-
href: 'https://epm.example.com/gateway/SyntheticAlpha/rest/v3/files/abc',
658+
it.each(['download', 'Job Status', 'exported/artifact', 'imported/artifact'])(
659+
'rejects an incorrect %s link method',
660+
(relation) => {
661+
const policy = routes.defineReturnedLinkPolicy({
662+
relation,
663+
method: 'GET',
664+
version: 'v3',
665+
path: [oracleEpmLiteral('files'), oracleEpmPathParameter('fileId', { maxBytes: 32 })],
666+
response: 'stream',
667+
timeoutMs: 5_000,
668+
maxResponseBytes: 4_096,
669+
preserveGatewayBasePath: true,
673670
})
674-
).toThrow()
675-
expect(mockValidateUrl).not.toHaveBeenCalled()
676-
expect(mockSecureFetch).not.toHaveBeenCalled()
677-
})
671+
const client = createOracleEpmClient({
672+
instanceUrl: 'https://epm.example.com/gateway',
673+
accessToken: Buffer.from('u:p').toString('base64'),
674+
})
675+
expect(() =>
676+
client.validateReturnedLink(policy, {
677+
rel: relation,
678+
method: 'POST',
679+
href: 'https://epm.example.com/gateway/SyntheticAlpha/rest/v3/files/abc',
680+
})
681+
).toThrow()
682+
expect(mockValidateUrl).not.toHaveBeenCalled()
683+
expect(mockSecureFetch).not.toHaveBeenCalled()
684+
}
685+
)
678686

679687
it.each([
680-
'job status',
681-
'Job status',
682-
' Job Status',
683-
'Job Status ',
684-
'Job Status',
685-
'Job\tStatus',
686-
'Job Status\n',
687-
'download',
688-
])('rejects nonmatching relation %j before DNS or network access', (rel) => {
688+
...[
689+
'job status',
690+
'Job status',
691+
' Job Status',
692+
'Job Status ',
693+
'Job Status',
694+
'Job\tStatus',
695+
'Job Status\n',
696+
'download',
697+
].map((rel) => ({ relation: 'Job Status', rel })),
698+
...[
699+
'Exported/artifact',
700+
'exported/Artifact',
701+
' exported/artifact',
702+
'exported/artifact ',
703+
'exported/artifact\n',
704+
'exported%2Fartifact',
705+
'exported\\artifact',
706+
'exported//artifact',
707+
'exported/artifact/extra',
708+
'imported/artifact',
709+
'self',
710+
].map((rel) => ({ relation: 'exported/artifact', rel })),
711+
])('rejects $rel for $relation before DNS or network access', ({ relation, rel }) => {
689712
const policy = routes.defineReturnedLinkPolicy({
690-
relation: 'Job Status',
713+
relation,
691714
method: 'GET',
692715
endpoint: getJob,
693716
preserveGatewayBasePath: true,

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,16 @@ describe('Oracle EPM returned-link declarations', () => {
4444
'a',
4545
'download',
4646
'report-content.v1_2',
47+
'exported/artifact',
48+
'imported/artifact',
49+
'Job/Status',
4750
'Job Status',
4851
'Download link',
4952
'Report Job Status',
5053
'Job 1.v2_3-4',
5154
'a'.repeat(64),
5255
`Job ${'a'.repeat(60)}`,
56+
`exported/${'a'.repeat(55)}`,
5357
])('preserves relation %j in a frozen policy', (relation) => {
5458
const policy = definePolicy(relation)
5559
const definition = getOracleEpmReturnedLinkPolicy(policy)
@@ -62,10 +66,12 @@ describe('Oracle EPM returned-link declarations', () => {
6266
'',
6367
'a'.repeat(65),
6468
`Job ${'a'.repeat(61)}`,
69+
`exported/${'a'.repeat(56)}`,
6570
'1Job',
6671
'.Job',
6772
'_Job',
6873
'-Job',
74+
'/artifact',
6975
' Job Status',
7076
'Job Status ',
7177
'Job Status',
@@ -83,8 +89,9 @@ describe('Oracle EPM returned-link declarations', () => {
8389
'Job\u200bStatus',
8490
'Job Status\u2028',
8591
'Job Status\u2029',
86-
'Job/Status',
8792
'Job\\Status',
93+
'exported/artifact\n',
94+
'exported%2Fartifact',
8895
'Job:Status',
8996
'Job%20Status',
9097
'Jób Status',

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import type {
1212
} from '@/lib/internal/oracle-epm/types'
1313

1414
const policies = new WeakMap<object, OracleEpmReturnedLinkPolicyDefinition>()
15-
/** Single ASCII spaces separate words; the final assertion rejects trailing line breaks too. */
16-
const RELATION = /^[A-Za-z][A-Za-z0-9._-]*(?: [A-Za-z0-9._-]+)*(?![\s\S])/
15+
/** Slashes are literal relation characters; single ASCII spaces separate words. */
16+
const RELATION = /^[A-Za-z][A-Za-z0-9._/-]*(?: [A-Za-z0-9._/-]+)*(?![\s\S])/
1717

1818
/** Internal frozen link policy available only after runtime-brand validation. */
1919
export interface OracleEpmReturnedLinkPolicyDefinition {

0 commit comments

Comments
 (0)