Skip to content

Commit e6fe8ec

Browse files
waleedlatif1claude
andcommitted
fix(identity-center): claim the ISO-F partition so Organizations never falls back to commercial
Widening the shared region validator to accept us-isof-* opened a path into getAwsPartition, which had no matching rule and fell through to the commercial default. resolveOrganizationsRegion then returned us-east-1 instead of throwing, sending an ISO-F caller's credentials cross-partition. Also drops an undocumented max on SSM listComplianceItems resourceTypes and corrects the SSM docs: written parameter values are recorded in the run log as block inputs unless supplied as an environment-variable reference. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LJV2dCcAYvf1JuxquCMZAd
1 parent c721fe5 commit e6fe8ec

8 files changed

Lines changed: 359 additions & 329 deletions

File tree

‎apps/docs/content/docs/integrations/ssm.mdx‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ In Sim, the Systems Manager integration is what lets an agent act on infrastruct
2626

2727
Parameter Store decryption is opt-in: `Get Parameter`, `Get Parameters`, and `Get Parameters By Path` leave `WithDecryption` off unless you explicitly enable it, so a SecureString stays encrypted by default.
2828

29-
Be precise about what that protects. The value you supply to `Put Parameter` is masked in the block, is never echoed back in the result, and no parameter value ever appears in an error message. A value you read is different: once you enable decryption, the plaintext is ordinary block output. It flows to downstream blocks as intended, and it is also written to the run log and the execution trace like any other output. Enable decryption only on the steps that genuinely need the plaintext, and treat those run logs as secret material.
29+
Be precise about what that protects, because it is narrower than it looks. The value you supply to `Put Parameter` is masked in the editor and is never echoed back in the operation's result, and Sim never puts a parameter value into an error message. It is **not** kept out of the run log: block inputs are recorded, and a value typed directly into the field is recorded verbatim. Referencing an environment variable instead — `{{MY_SECRET}}` — keeps the literal out of the log, because references are restored to their placeholder before the log is written.
30+
31+
Reads are exposed the same way. Once you enable decryption the plaintext is ordinary block output: it flows to downstream blocks as intended, and it is written to the run log and the execution trace like any other output.
32+
33+
So enable decryption only on the steps that genuinely need the plaintext, prefer environment-variable references over typed literals when writing, and treat the run logs of any workflow that touches SecureString values as secret material.
3034
{/* MANUAL-CONTENT-END */}
3135

3236

‎apps/sim/lib/api/contracts/tools/aws/ssm-list-compliance-items.ts‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,13 @@ const RequestSchema = z.object({
3636
}),
3737
accessKeyId: z.string().min(1, 'AWS access key ID is required'),
3838
secretAccessKey: z.string().min(1, 'AWS secret access key is required'),
39+
/**
40+
* AWS documents "you can only specify one resource ID per call" for `ResourceIds`.
41+
* `ResourceTypes` publishes a minimum of 1 item and no maximum, so it is left
42+
* unbounded rather than inheriting a cap AWS never documented.
43+
*/
3944
resourceIds: z.array(z.string().min(1)).max(1).nullish(),
40-
resourceTypes: z.array(z.string().min(1)).max(1).nullish(),
45+
resourceTypes: z.array(z.string().min(1)).min(1).nullish(),
4146
filters: z.array(ComplianceStringFilterSchema).nullish(),
4247
maxResults: z.number().int().min(1).max(50).nullish(),
4348
nextToken: z.string().nullish(),

‎apps/sim/lib/internal/identity-center/partition.test.ts‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* @vitest-environment node
33
*/
44
import { describe, expect, it } from 'vitest'
5+
import { validateAwsRegion } from '@/lib/core/security/input-validation'
56
import {
67
getAwsPartition,
78
resolveOrganizationsRegion,
@@ -18,11 +19,39 @@ describe('getAwsPartition', () => {
1819
['cn-northwest-1', 'aws-cn'],
1920
['us-iso-east-1', 'aws-iso'],
2021
['us-isob-east-1', 'aws-iso-b'],
22+
['us-isof-east-1', 'aws-iso-f'],
23+
['us-isof-south-1', 'aws-iso-f'],
2124
['eu-isoe-west-1', 'aws-iso-e'],
2225
['eusc-de-east-1', 'aws-eusc'],
2326
])('maps %s to the %s partition', (region, partition) => {
2427
expect(getAwsPartition(region)).toBe(partition)
2528
})
29+
30+
/**
31+
* `getAwsPartition` falls through to the commercial `aws` partition for any region
32+
* it does not recognize, so widening `validateAwsRegion` without adding the matching
33+
* rule here silently routes an isolated-partition caller to a commercial endpoint.
34+
* This couples the two so that drift fails the suite instead of shipping.
35+
*/
36+
it('claims a non-commercial partition for every isolated region the shared validator admits', () => {
37+
const isolatedRegions = [
38+
'us-gov-west-1',
39+
'us-gov-east-1',
40+
'cn-north-1',
41+
'cn-northwest-1',
42+
'us-iso-east-1',
43+
'us-isob-east-1',
44+
'us-isof-east-1',
45+
'us-isof-south-1',
46+
'eu-isoe-west-1',
47+
'eusc-de-east-1',
48+
]
49+
50+
for (const region of isolatedRegions) {
51+
expect(validateAwsRegion(region).isValid).toBe(true)
52+
expect(getAwsPartition(region)).not.toBe('aws')
53+
}
54+
})
2655
})
2756

2857
describe('resolveOrganizationsRegion', () => {

‎apps/sim/lib/internal/identity-center/partition.ts‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export type AwsPartition =
99
| 'aws-iso'
1010
| 'aws-iso-b'
1111
| 'aws-iso-e'
12+
| 'aws-iso-f'
1213
| 'aws-eusc'
1314

1415
interface PartitionRule {
@@ -17,12 +18,16 @@ interface PartitionRule {
1718
}
1819

1920
/**
20-
* Ordered longest-prefix-first so `us-isob-east-1` is not mistaken for `us-iso-*`.
21-
* Regions that match no prefix belong to the commercial `aws` partition.
21+
* Ordered longest-prefix-first so `us-isob-east-1` and `us-isof-south-1` are not
22+
* mistaken for `us-iso-*`. Regions that match no prefix belong to the commercial
23+
* `aws` partition, so every isolated partition the shared region validator admits
24+
* must appear here — otherwise it falls through to `aws` and
25+
* `resolveOrganizationsRegion` hands back a commercial endpoint instead of throwing.
2226
*/
2327
const PARTITION_RULES: readonly PartitionRule[] = [
2428
{ prefix: 'us-gov-', partition: 'aws-us-gov' },
2529
{ prefix: 'us-isob-', partition: 'aws-iso-b' },
30+
{ prefix: 'us-isof-', partition: 'aws-iso-f' },
2631
{ prefix: 'us-iso-', partition: 'aws-iso' },
2732
{ prefix: 'eu-isoe-', partition: 'aws-iso-e' },
2833
{ prefix: 'eusc-', partition: 'aws-eusc' },

‎apps/sim/tools/generated/tool-ids.ts‎

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

‎apps/sim/tools/generated/tool-metadata.ts‎

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

‎apps/sim/tools/generated/tool-outputs.ts‎

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)