Skip to content

Commit 2864a4d

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
feat(oci): support multi-label service prefixes
1 parent 9abff14 commit 2864a4d

2 files changed

Lines changed: 151 additions & 3 deletions

File tree

apps/sim/lib/internal/oci/endpoints.test.ts

Lines changed: 137 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,142 @@ describe('OCI endpoint policies', () => {
9393
})
9494
})
9595

96+
it.each(['secrets.vaults', 'ingestion.logging', 'identity', 'telemetry-ingestion', 'a', 'a0'])(
97+
'constructs exact regional hosts for the service prefix %s',
98+
(serviceName) => {
99+
const policy = createOciStaticEndpointPolicy({
100+
serviceId: OCI_SERVICE_ID,
101+
serviceName,
102+
hostnameTemplate: 'regional-oci',
103+
})
104+
expect(Object.isFrozen(policy)).toBe(true)
105+
expect(policy.serviceName).toBe(serviceName)
106+
for (const regionId of ['us-ashburn-1', 'us-gov-ashburn-1']) {
107+
const selectedRegion = getOciRegion(regionId)
108+
expect(resolveStaticOciEndpoint(policy, selectedRegion).origin).toBe(
109+
`https://${serviceName}.${regionId}.oci.${selectedRegion.realm.domain}`
110+
)
111+
}
112+
}
113+
)
114+
115+
it('preserves multi-label ownership in authenticated discovery', () => {
116+
const policy = createOciDiscoveredEndpointPolicy({
117+
serviceId: OCI_SERVICE_ID,
118+
serviceName: 'secrets.vaults',
119+
hostnameTemplate: 'regional-oci',
120+
responsePolicy: staticPolicy,
121+
source: { kind: 'json', path: ['endpoint'] },
122+
})
123+
expect(Object.isFrozen(policy)).toBe(true)
124+
expect(
125+
resolveDiscoveredOciEndpoint(
126+
policy,
127+
region,
128+
'https://resource.secrets.vaults.us-ashburn-1.oci.oraclecloud.com'
129+
).serviceName
130+
).toBe('secrets.vaults')
131+
for (const origin of [
132+
'https://resource.vaults.us-ashburn-1.oci.oraclecloud.com',
133+
'https://resource.secrets.vaults.eu-frankfurt-1.oci.oraclecloud.com',
134+
'https://resource.secrets.vaults.us-ashburn-1.oci.oraclegovcloud.com',
135+
'https://resource.secrets.vaults.us-ashburn-1.oci.oraclecloud.com.attacker.example',
136+
]) {
137+
expect(() => resolveDiscoveredOciEndpoint(policy, region, origin)).toThrow()
138+
}
139+
})
140+
141+
it.each([
142+
'',
143+
'.',
144+
'.identity',
145+
'identity.',
146+
'secrets..vaults',
147+
'-identity',
148+
'identity-',
149+
'1identity',
150+
'Identity',
151+
'identity_service',
152+
'identity service',
153+
'identity\n',
154+
'identity\r',
155+
'identity\t',
156+
'identity\0',
157+
'identité',
158+
'https://identity',
159+
'identity:443',
160+
'identity/path',
161+
'identity\\path',
162+
'*.identity',
163+
'identity?x=1',
164+
'identity#fragment',
165+
'identity@host',
166+
'a'.repeat(64),
167+
null,
168+
undefined,
169+
42,
170+
['identity'],
171+
])('rejects malformed service prefixes in both policy factories: %j', (serviceName) => {
172+
expect(() =>
173+
createOciStaticEndpointPolicy({
174+
serviceId: OCI_SERVICE_ID,
175+
serviceName: serviceName as never,
176+
hostnameTemplate: 'regional',
177+
})
178+
).toThrow('service name')
179+
expect(() =>
180+
createOciDiscoveredEndpointPolicy({
181+
serviceId: OCI_SERVICE_ID,
182+
serviceName: serviceName as never,
183+
hostnameTemplate: 'regional',
184+
responsePolicy: staticPolicy,
185+
source: { kind: 'json', path: ['endpoint'] },
186+
})
187+
).toThrow('service name')
188+
})
189+
190+
it('bounds labels, prefixes, and complete hostnames', () => {
191+
const label = 'a'.repeat(63)
192+
expect(regionalOciHostname(label, region, 'regional')).toBe(
193+
`${label}.${region.id}.${region.realm.domain}`
194+
)
195+
const prefix = [label, label, label, 'b'.repeat(61)].join('.')
196+
expect(prefix.length).toBe(253)
197+
const policy = createOciStaticEndpointPolicy({
198+
serviceId: OCI_SERVICE_ID,
199+
serviceName: prefix,
200+
hostnameTemplate: 'regional',
201+
})
202+
expect(policy.serviceName).toBe(prefix)
203+
expect(() =>
204+
createOciStaticEndpointPolicy({
205+
...policy,
206+
serviceName: `${prefix}b`,
207+
})
208+
).toThrow('service name')
209+
expect(() => resolveStaticOciEndpoint(policy, region)).toThrow('hostname')
210+
211+
const suffix = `.${region.id}.oci.${region.realm.domain}`
212+
const boundedPrefix = [label, label, label, 'b'.repeat(253 - suffix.length - 192)].join('.')
213+
const boundedPolicy = createOciStaticEndpointPolicy({
214+
serviceId: OCI_SERVICE_ID,
215+
serviceName: boundedPrefix,
216+
hostnameTemplate: 'regional-oci',
217+
})
218+
expect(resolveStaticOciEndpoint(boundedPolicy, region).hostname.length).toBe(253)
219+
expect(() => regionalOciHostname(`${boundedPrefix}b`, region, 'regional-oci')).toThrow(
220+
'hostname'
221+
)
222+
const boundedDiscovery = createOciDiscoveredEndpointPolicy({
223+
...boundedPolicy,
224+
responsePolicy: staticPolicy,
225+
source: { kind: 'json', path: ['endpoint'] },
226+
})
227+
expect(() =>
228+
resolveDiscoveredOciEndpoint(boundedDiscovery, region, `https://a.${boundedPrefix}${suffix}`)
229+
).toThrow()
230+
})
231+
96232
it.each([
97233
'http://resource.database.us-ashburn-1.oraclecloud.com',
98234
'https://resource.database.us-ashburn-1.oraclecloud.com:8443',
@@ -128,7 +264,7 @@ describe('OCI endpoint policies', () => {
128264
expect(() =>
129265
createOciStaticEndpointPolicy({
130266
serviceId: OCI_SERVICE_ID,
131-
serviceName: 'bad.name',
267+
serviceName: 'bad..name',
132268
hostnameTemplate: 'regional',
133269
})
134270
).toThrow('service name')

apps/sim/lib/internal/oci/endpoints.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ const REGION_REALMS = {
176176
} as const satisfies Record<string, OciRealmId>
177177

178178
export const OCI_REGION_IDS = Object.freeze(Object.keys(REGION_REALMS))
179+
const MAX_HOSTNAME_LENGTH = 253
179180

180181
function normalizeRegionId(regionId: string): string {
181182
return regionId.trim().toLowerCase()
@@ -203,7 +204,13 @@ export function resolveEffectiveOciRegion(defaultRegion: string, override?: stri
203204
}
204205

205206
function assertServiceName(value: string): void {
206-
if (!/^[a-z][a-z0-9-]{0,62}$/.test(value)) {
207+
if (
208+
typeof value !== 'string' ||
209+
value.length === 0 ||
210+
value.length > MAX_HOSTNAME_LENGTH ||
211+
/[^a-z0-9.-]/.test(value) ||
212+
value.split('.').some((label) => !/^[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(label))
213+
) {
207214
throw new Error('OCI endpoint policy service name is invalid')
208215
}
209216
}
@@ -288,7 +295,11 @@ export function regionalOciHostname(
288295
assertServiceName(serviceName)
289296
assertHostnameTemplate(hostnameTemplate)
290297
const ociLabel = hostnameTemplate === 'regional-oci' ? '.oci' : ''
291-
return `${serviceName}.${region.id}${ociLabel}.${region.realm.domain}`
298+
const hostname = `${serviceName}.${region.id}${ociLabel}.${region.realm.domain}`
299+
if (hostname.length > MAX_HOSTNAME_LENGTH) {
300+
throw new Error('OCI endpoint policy hostname is invalid')
301+
}
302+
return hostname
292303
}
293304

294305
function validateOciOrigin(params: {
@@ -319,6 +330,7 @@ function validateOciOrigin(params: {
319330
url.pathname !== '/' ||
320331
url.search !== '' ||
321332
url.hash !== '' ||
333+
url.hostname.length > MAX_HOSTNAME_LENGTH ||
322334
isIpLiteral(unwrapIpv6Brackets(url.hostname)) ||
323335
url.origin !== params.origin
324336
) {

0 commit comments

Comments
 (0)