@fjall/generator 30.0.0 → 31.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,7 @@
9
9
  "package.json",
10
10
  "tsconfig.json"
11
11
  ],
12
- "hash": "2cdc6fe6364ffca7659f2905b7e3efb325d995e5d44ad130ece892073f4aac34",
12
+ "hash": "f6fe0cf1bd62c37ef4f0bd6a7bdb0214ebec66f14fe6564df2548372a4e25ca8",
13
13
  "files": {
14
14
  "src/ast/astTestHelpers.ts": "026cac849eb88bdb",
15
15
  "src/ast/domain/astDomainParser.ts": "12107a9864c6670c",
@@ -173,7 +173,7 @@
173
173
  "src/schemas/organisationSchemas.ts": "50d793aa7ff7f749",
174
174
  "src/schemas/patternSchemas.ts": "60a32bacaf767479",
175
175
  "src/schemas/resourceSchemas.ts": "cd050813242ea213",
176
- "src/schemas/securityBoundary.ts": "54d216807103bab8",
176
+ "src/schemas/securityBoundary.ts": "ef3148c29d5d11f3",
177
177
  "src/schemas/sharedTypes.ts": "45210e600bea393e",
178
178
  "src/schemas/storageSchemas.ts": "2d4b9d86e3397387",
179
179
  "src/types/Result.ts": "6a078a91fc3072e3",
@@ -181,11 +181,11 @@
181
181
  "src/validation/patterns.ts": "f9629eafdad7ac3e",
182
182
  "src/validation/validationMessages.ts": "b03413b3953c3e79",
183
183
  "src/validation/validationPatterns.ts": "865f3d688975b7c8",
184
- "src/version.ts": "2b53ee70cfa3c998",
184
+ "src/version.ts": "44796fa69ed21d3a",
185
185
  "../scripts/minify-dist.mjs": "6b2e4b0df8aec601",
186
- "package.json": "0d9765f4ee24793d",
186
+ "package.json": "d78da7528d41e1e1",
187
187
  "tsconfig.json": "1929cf3f99fe9b1e"
188
188
  },
189
- "outputHash": "2c7b712a79479ee7fd8b2f55ad95efb5f6c3d09c9f1052395ead03aeb8cff99e",
189
+ "outputHash": "49be50db43b701a3a7c54d94132304d0ecce076bc9c30df481ed694a87cce1e4",
190
190
  "outputFileCount": 343
191
191
  }
package/dist/.minified CHANGED
@@ -1 +1 @@
1
- 171 files minified at 2026-09-02T22:02:58.560Z
1
+ 171 files minified at 2026-09-03T09:13:58.800Z
@@ -12,11 +12,13 @@ import { type GovernancePreset } from "./constants.js";
12
12
  * and per-tier STATEMENT CONTENT. It lives in `@fjall/generator` (not
13
13
  * `@fjall/util`) because it keys on `GovernancePreset`, which lives here — and
14
14
  * `@fjall/util` cannot import it without a circular dependency. Consumers:
15
- * `@fjall/components-infrastructure` (creates the policy in `oidcConnector.ts`),
15
+ * `@fjall/components-infrastructure` (renders the policy document into the
16
+ * `Custom::FjallDeployBoundary` singleton in `oidcConnector.ts`),
16
17
  * `@fjall/deploy-core` (passes the name to `cdk bootstrap`), `@fjall/cli`
17
18
  * (resolves the tier). The Quick-Create YAML twin mirrors the content by hand,
18
19
  * kept in lockstep by `oidcConnectorTemplate.test.ts` (presence parity + a
19
- * statement-content deep-compare against the hardened tier of this SSoT).
20
+ * statement-content deep-compare against this SSoT's
21
+ * `DEFAULT_DEPLOY_BOUNDARY_TIER`).
20
22
  *
21
23
  * NOT related to `PermissionsBoundarySpec` in organisationSchemas.ts — that is a
22
24
  * REFERENCE to an existing policy for Identity Centre permission sets; this
@@ -53,6 +55,63 @@ export interface BoundaryStatement {
53
55
  * (`CdkService.runCdkBootstrap` → `CdkOptions.permissionsBoundary`).
54
56
  */
55
57
  export declare function deployBoundaryName(fjallOrgId: string): string;
58
+ /**
59
+ * IAM path of the OIDC deploy role. Load-bearing: the org SCP preset exempts
60
+ * `role/fjall/FjallDeploy*` from its protect-* denies BY PATH, so a
61
+ * member-account admin cannot forge an exempt `FjallDeploy*` role at the
62
+ * default path.
63
+ */
64
+ export declare const DEPLOY_ROLE_PATH = "/fjall/";
65
+ /**
66
+ * The OIDC deploy role's name — org-id-suffixed like the boundary it carries.
67
+ *
68
+ * COUPLED VALUE — the CDK construct that CREATES the role
69
+ * (`config/aws/oidcConnector.ts`), the SCP preset's exempt pattern
70
+ * (`config/aws/scpPreset.ts`) and `DEPLOY_BOUNDED_ROLE_ARN_PATTERNS` below
71
+ * all derive from here; the Quick-Create YAML twin
72
+ * (`!Sub "FjallDeploy${FjallOrgId}"`) mirrors it by hand, parity-pinned.
73
+ */
74
+ export declare function deployRoleName(fjallOrgId: string): string;
75
+ /**
76
+ * ARN shape of every org's deploy-boundary policy, any partition, any account
77
+ * — the resource the self-protection statements below key on. Derived from
78
+ * `deployBoundaryName` so a name change moves the protection with it.
79
+ */
80
+ export declare const DEPLOY_BOUNDARY_POLICY_ARN_PATTERN: string;
81
+ /**
82
+ * ARN shapes of the two admin surfaces whose boundary Fjall OWNS: the OIDC
83
+ * deploy role (`deployRoleName` under `DEPLOY_ROLE_PATH` — the derivation
84
+ * `oidcConnector.ts` renders, so a rename moves the protection with it) and
85
+ * the CDK bootstrap execution role
86
+ * (`cdk-<qualifier>-cfn-exec-role-<account>-<region>`, capped by seam 2).
87
+ * `FjallDenyBoundaryReplace` is scoped to these two, never `*`: CloudFormation
88
+ * emits `iam:PutRolePermissionsBoundary` when a customer stack UPDATES the
89
+ * boundary on one of its OWN roles (an org-mandated enterprise boundary), and a
90
+ * global deny would break that legitimate deploy. Revisit the scope when E3b
91
+ * bounds every CDK-created role with a Fjall policy — and note that a
92
+ * scope-only change to a statement (same Deny actions, wider Resource or a
93
+ * dropped Condition) is invisible to the singleton writer's loosening ratchet,
94
+ * which compares Deny ACTION sets (`deployBoundarySingleton.ts`): it lands as
95
+ * an ordinary tier rotation, so it needs the digest rotation, never a hand
96
+ * edit of the live policy.
97
+ */
98
+ export declare const DEPLOY_BOUNDED_ROLE_ARN_PATTERNS: string[];
99
+ /**
100
+ * The boundary CONTENT every renderer of the deploy surface ships when no
101
+ * governance tier is given — the default is a TIER, never "no boundary".
102
+ *
103
+ * COUPLED VALUE — three renderers MUST agree on it, or a CDK redeploy of a
104
+ * template-connected role silently loosens (or tightens) the ceiling the
105
+ * customer connected under: (1) the Quick-Create YAML twin, which has no tier
106
+ * parameter and hand-mirrors exactly this tier's statements (deep-compared by
107
+ * `oidcConnectorTemplate.test.ts`); (2) the CDK `OidcConnector`, which reads
108
+ * it when `securityTier` is absent; (3) the bootstrap seam, which caps the
109
+ * `cfn-exec-role` with whichever policy (1) or (2) created. `securityTier`
110
+ * selects different CONTENT; the `deployRoleBoundary: false` /
111
+ * `FjallPermissionsBoundary=false` knobs are the only way to render an
112
+ * unbounded role.
113
+ */
114
+ export declare const DEFAULT_DEPLOY_BOUNDARY_TIER: GovernancePreset;
56
115
  /**
57
116
  * Tier → boundary statement list. `Record<GovernancePreset, …>` so a new tier
58
117
  * landing in `GOVERNANCE_PRESETS` lights up a missing-key `error TS2741` here —
@@ -65,16 +124,6 @@ export declare function deployBoundaryName(fjallOrgId: string): string;
65
124
  * (adversarial content review, aiDocs 2026-07-05-phase-e-deploy-boundary):
66
125
  * • `iam:AttachRolePolicy` of `AdministratorAccess` — breaks bootstrap's
67
126
  * creation of the cfn-exec-role (which carries AdministratorAccess).
68
- * • `iam:PutRolePermissionsBoundary` [companion to FjallDenyBoundarySelfEscape]
69
- * — the SECOND self-escape (replace own boundary with a permissive one).
70
- * Emitted by re-bootstrap boundary UPDATE and by the deferred E3b path
71
- * (cdk.json permissionsBoundary on app roles → CFN UPDATE of an existing
72
- * role's boundary). Deny only once E3b resource-scopes it (allow when
73
- * setting the Fjall boundary / exempt CDK-created role paths); hardened.
74
- * • Boundary-policy self-protection (deny `iam:CreatePolicyVersion` /
75
- * `iam:SetDefaultPolicyVersion` on the FjallDeployBoundary policy itself)
76
- * — blocks the role republishing a permissive version of its own ceiling;
77
- * requires out-of-band admin to re-tune, so land with the live loop.
78
127
  * • `kms:PutKeyPolicy` — CFN emits it on key-policy/grant UPDATE (e.g.
79
128
  * `key.grantDecrypt(newPrincipal)` on redeploy); denying outright breaks a
80
129
  * legitimate app redeploy. Needs a live cycle or resource scoping.
@@ -91,6 +140,32 @@ export declare function deployBoundaryName(fjallOrgId: string): string;
91
140
  * UpdateOrganizationConfiguration), but they fire only in a platform/org
92
141
  * account; validate in a platform hardened cycle.
93
142
  *
143
+ * LANDED FROM THE CANDIDATE LIST (foundation, 31.0.0): the two companion
144
+ * self-escapes. `iam:PutRolePermissionsBoundary` on the two Fjall-bounded
145
+ * roles is denied only when the boundary being set is NOT a
146
+ * `FjallDeployBoundary*` policy (`FjallDenyBoundaryReplace`, an `ArnNotLike`
147
+ * condition on `iam:PermissionsBoundary`), so re-bootstrap's boundary UPDATE
148
+ * and the deferred E3b path — both of which set the Fjall boundary — stay
149
+ * allowed, and so does a customer stack putting its own boundary on its own
150
+ * roles. Create/re-version/delete of the boundary policy itself is denied
151
+ * outright (`FjallDenyBoundaryPolicyTamper`); the policy is written by the
152
+ * `Custom::FjallDeployBoundary` singleton handler — a bounded cfn-exec-role no
153
+ * longer creates or updates it natively — and that handler publishes only a
154
+ * document whose digest matches a tier below, so driving it with a crafted
155
+ * custom-resource event cannot loosen the ceiling either. Both denies are
156
+ * one-way for the bounded principal: re-tuning the ceiling is an out-of-band
157
+ * admin step (the handler converges the policy on the next connector deploy).
158
+ *
159
+ * HONEST CEILING. The boundary stops accidental and negligent CloudFormation
160
+ * actions and the direct self-edits above. It is NOT a hard cap against a
161
+ * hostile bounded session: the same AdministratorAccess-minus-denies session
162
+ * can still `iam:UpdateAssumeRolePolicy` + `sts:AssumeRole` an unbounded
163
+ * pre-existing role (OrganizationAccountAccessRole exists in every member
164
+ * account), or rewrite the singleton handler's code before the next deploy
165
+ * invokes it. Closing those needs the deferred E3b (boundary on every
166
+ * CDK-created role) plus role-immutability denies, which today would break
167
+ * bootstrap's creation of the cfn-exec-role — tracked, not built.
168
+ *
94
169
  * NEVER DENY (completeness review verified these are LOAD-BEARING — the org
95
170
  * security-services deploy CALLS them with AutoEnableOrganizationMembers:'ALL',
96
171
  * so denying breaks a legitimate platform-org deploy):
@@ -1 +1 @@
1
- var a=Object.defineProperty;var o=(e,t)=>a(e,"name",{value:t,configurable:!0});function r(e){return`FjallDeployBoundary${e}`}o(r,"deployBoundaryName");const n={Sid:"FjallInheritAdministratorAccess",Effect:"Allow",Action:"*",Resource:"*"},c=[{Sid:"FjallDenyOrgAndAccountEscape",Effect:"Deny",Action:["organizations:LeaveOrganization","account:CloseAccount"],Resource:"*"},{Sid:"FjallDenyDisableAuditLogging",Effect:"Deny",Action:["cloudtrail:StopLogging"],Resource:"*"},{Sid:"FjallDenyBoundarySelfEscape",Effect:"Deny",Action:["iam:DeleteRolePermissionsBoundary"],Resource:"*"},{Sid:"FjallDenyDisableKmsKey",Effect:"Deny",Action:["kms:DisableKey"],Resource:"*"}],i=[...c,{Sid:"FjallDenyStandingHumanCredentials",Effect:"Deny",Action:["iam:CreateUser","iam:CreateLoginProfile","iam:CreateAccessKey","iam:CreateAccountAlias"],Resource:"*"},{Sid:"FjallDenyDisableConfigRecorder",Effect:"Deny",Action:["config:StopConfigurationRecorder"],Resource:"*"}],l=[...i,{Sid:"FjallDenyAccountPasswordPolicyTamper",Effect:"Deny",Action:["iam:UpdateAccountPasswordPolicy","iam:DeleteAccountPasswordPolicy"],Resource:"*"},{Sid:"FjallDenyDisableDefaultEbsEncryption",Effect:"Deny",Action:["ec2:DisableEbsEncryptionByDefault"],Resource:"*"}],D={foundation:[n,...c],compliance:[n,...i],hardened:[n,...l]};export{D as SECURITY_TIER_TO_BOUNDARY,r as deployBoundaryName};
1
+ var r=Object.defineProperty;var o=(e,t)=>r(e,"name",{value:t,configurable:!0});function l(e){return`FjallDeployBoundary${e}`}o(l,"deployBoundaryName");const s="/fjall/";function D(e){return`FjallDeploy${e}`}o(D,"deployRoleName");const i=`arn:*:iam::*:policy/${l("*")}`,y=[`arn:*:iam::*:role${s}${D("*")}`,"arn:*:iam::*:role/cdk-*-cfn-exec-role-*"],n={Sid:"FjallInheritAdministratorAccess",Effect:"Allow",Action:"*",Resource:"*"},c=[{Sid:"FjallDenyOrgAndAccountEscape",Effect:"Deny",Action:["organizations:LeaveOrganization","account:CloseAccount"],Resource:"*"},{Sid:"FjallDenyDisableAuditLogging",Effect:"Deny",Action:["cloudtrail:StopLogging"],Resource:"*"},{Sid:"FjallDenyBoundarySelfEscape",Effect:"Deny",Action:["iam:DeleteRolePermissionsBoundary"],Resource:"*"},{Sid:"FjallDenyBoundaryReplace",Effect:"Deny",Action:["iam:PutRolePermissionsBoundary"],Resource:y,Condition:{ArnNotLike:{"iam:PermissionsBoundary":i}}},{Sid:"FjallDenyBoundaryPolicyTamper",Effect:"Deny",Action:["iam:CreatePolicy","iam:CreatePolicyVersion","iam:DeletePolicyVersion","iam:SetDefaultPolicyVersion","iam:DeletePolicy"],Resource:i},{Sid:"FjallDenyDisableKmsKey",Effect:"Deny",Action:["kms:DisableKey"],Resource:"*"}],a=[...c,{Sid:"FjallDenyStandingHumanCredentials",Effect:"Deny",Action:["iam:CreateUser","iam:CreateLoginProfile","iam:CreateAccessKey","iam:CreateAccountAlias"],Resource:"*"},{Sid:"FjallDenyDisableConfigRecorder",Effect:"Deny",Action:["config:StopConfigurationRecorder"],Resource:"*"}],d=[...a,{Sid:"FjallDenyAccountPasswordPolicyTamper",Effect:"Deny",Action:["iam:UpdateAccountPasswordPolicy","iam:DeleteAccountPasswordPolicy"],Resource:"*"},{Sid:"FjallDenyDisableDefaultEbsEncryption",Effect:"Deny",Action:["ec2:DisableEbsEncryptionByDefault"],Resource:"*"}],A="hardened",E={foundation:[n,...c],compliance:[n,...a],hardened:[n,...d]};export{A as DEFAULT_DEPLOY_BOUNDARY_TIER,i as DEPLOY_BOUNDARY_POLICY_ARN_PATTERN,y as DEPLOY_BOUNDED_ROLE_ARN_PATTERNS,s as DEPLOY_ROLE_PATH,E as SECURITY_TIER_TO_BOUNDARY,l as deployBoundaryName,D as deployRoleName};
@@ -1 +1 @@
1
- export declare const GENERATOR_VERSION = "30.0.0";
1
+ export declare const GENERATOR_VERSION = "31.0.0";
@@ -1 +1 @@
1
- const E="30.0.0";export{E as GENERATOR_VERSION};
1
+ const E="31.0.0";export{E as GENERATOR_VERSION};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fjall/generator",
3
- "version": "30.0.0",
3
+ "version": "31.0.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/fjall-tech/fjall.git",
@@ -58,7 +58,7 @@
58
58
  "license": "SEE LICENSE IN LICENSE",
59
59
  "dependencies": {
60
60
  "@babel/parser": "^7.29.8",
61
- "@fjall/util": "^30.0.0",
61
+ "@fjall/util": "^31.0.0",
62
62
  "ast-types": "^0.16.1",
63
63
  "recast": "^0.23.12",
64
64
  "ts-morph": "^28.0.0",