@go-to-k/cdkd 0.0.3 → 0.0.4

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.
package/dist/cli.js CHANGED
@@ -3519,6 +3519,11 @@ var TemplateParser = class {
3519
3519
 
3520
3520
  // src/analyzer/dag-builder.ts
3521
3521
  var { Graph, alg } = graphlib;
3522
+ var IAM_ROLE_POLICY_TYPES = /* @__PURE__ */ new Set([
3523
+ "AWS::IAM::Policy",
3524
+ "AWS::IAM::RolePolicy",
3525
+ "AWS::IAM::ManagedPolicy"
3526
+ ]);
3522
3527
  var DagBuilder = class {
3523
3528
  logger = getLogger().child("DagBuilder");
3524
3529
  parser = new TemplateParser();
@@ -3559,6 +3564,7 @@ var DagBuilder = class {
3559
3564
  }
3560
3565
  }
3561
3566
  this.logger.debug(`Dependency graph built: ${resourceIds.length} nodes, ${edgeCount} edges`);
3567
+ edgeCount += this.addCustomResourcePolicyEdges(graph, template);
3562
3568
  if (!alg.isAcyclic(graph)) {
3563
3569
  const cycles = this.findCycles(graph);
3564
3570
  throw new DependencyError(
@@ -3701,6 +3707,123 @@ var DagBuilder = class {
3701
3707
  const deps = this.getAllDependencies(graph, resourceA);
3702
3708
  return deps.has(resourceB);
3703
3709
  }
3710
+ /**
3711
+ * Add implicit edges from IAM::Policy resources to Custom Resources whose
3712
+ * ServiceToken Lambda's execution role those policies attach to.
3713
+ *
3714
+ * Returns the number of edges added.
3715
+ */
3716
+ addCustomResourcePolicyEdges(graph, template) {
3717
+ const rolePolicies = this.buildRolePoliciesMap(template);
3718
+ if (rolePolicies.size === 0) {
3719
+ return 0;
3720
+ }
3721
+ let added = 0;
3722
+ for (const logicalId of this.parser.getResourceIds(template)) {
3723
+ const resource = this.parser.getResource(template, logicalId);
3724
+ if (!resource || !this.isCustomResourceType(resource.Type)) {
3725
+ continue;
3726
+ }
3727
+ const serviceToken = (resource.Properties ?? {})["ServiceToken"];
3728
+ const lambdaId = this.extractLogicalIdFromReference(serviceToken);
3729
+ if (!lambdaId)
3730
+ continue;
3731
+ const lambdaResource = this.parser.getResource(template, lambdaId);
3732
+ if (!lambdaResource || lambdaResource.Type !== "AWS::Lambda::Function") {
3733
+ continue;
3734
+ }
3735
+ const roleId = this.extractLogicalIdFromReference((lambdaResource.Properties ?? {})["Role"]);
3736
+ if (!roleId)
3737
+ continue;
3738
+ const policies = rolePolicies.get(roleId);
3739
+ if (!policies)
3740
+ continue;
3741
+ for (const policyId of policies) {
3742
+ if (policyId === logicalId)
3743
+ continue;
3744
+ if (!graph.hasNode(policyId))
3745
+ continue;
3746
+ if (graph.hasEdge(policyId, logicalId))
3747
+ continue;
3748
+ graph.setEdge(policyId, logicalId);
3749
+ added++;
3750
+ this.logger.debug(
3751
+ `Added implicit edge (custom resource policy): ${policyId} -> ${logicalId}`
3752
+ );
3753
+ }
3754
+ }
3755
+ if (added > 0) {
3756
+ this.logger.debug(`Added ${added} implicit edges for custom resource policies`);
3757
+ }
3758
+ return added;
3759
+ }
3760
+ isCustomResourceType(type) {
3761
+ return type === "AWS::CloudFormation::CustomResource" || type.startsWith("Custom::");
3762
+ }
3763
+ /**
3764
+ * Build a map of roleLogicalId -> Set<policyLogicalId> by scanning the
3765
+ * template for IAM::Policy / IAM::RolePolicy / IAM::ManagedPolicy resources
3766
+ * that attach to a role by Ref/GetAtt.
3767
+ */
3768
+ buildRolePoliciesMap(template) {
3769
+ const map = /* @__PURE__ */ new Map();
3770
+ for (const [policyId, resource] of Object.entries(template.Resources)) {
3771
+ if (!IAM_ROLE_POLICY_TYPES.has(resource.Type))
3772
+ continue;
3773
+ for (const roleId of this.extractAttachedRoleIds(resource)) {
3774
+ let set = map.get(roleId);
3775
+ if (!set) {
3776
+ set = /* @__PURE__ */ new Set();
3777
+ map.set(roleId, set);
3778
+ }
3779
+ set.add(policyId);
3780
+ }
3781
+ }
3782
+ return map;
3783
+ }
3784
+ /**
3785
+ * Extract the logical IDs of IAM::Role resources that a policy resource
3786
+ * attaches to. Supports both `Roles: [Ref]` (IAM::Policy / IAM::ManagedPolicy)
3787
+ * and `RoleName: Ref` (IAM::RolePolicy) shapes.
3788
+ */
3789
+ extractAttachedRoleIds(resource) {
3790
+ const ids = [];
3791
+ const props = resource.Properties ?? {};
3792
+ const roles = props["Roles"];
3793
+ if (Array.isArray(roles)) {
3794
+ for (const entry of roles) {
3795
+ const id = this.extractLogicalIdFromReference(entry);
3796
+ if (id)
3797
+ ids.push(id);
3798
+ }
3799
+ }
3800
+ const roleName = props["RoleName"];
3801
+ const roleNameId = this.extractLogicalIdFromReference(roleName);
3802
+ if (roleNameId)
3803
+ ids.push(roleNameId);
3804
+ return ids;
3805
+ }
3806
+ /**
3807
+ * Extract a resource logical ID from a direct Ref or Fn::GetAtt expression.
3808
+ * Returns undefined for literals or intrinsics we can't statically resolve
3809
+ * (Fn::Join, Fn::ImportValue, etc.) — callers should skip in that case.
3810
+ */
3811
+ extractLogicalIdFromReference(value) {
3812
+ if (typeof value !== "object" || value === null)
3813
+ return void 0;
3814
+ const obj = value;
3815
+ if ("Ref" in obj && typeof obj["Ref"] === "string") {
3816
+ const ref = obj["Ref"];
3817
+ return ref.startsWith("AWS::") ? void 0 : ref;
3818
+ }
3819
+ if ("Fn::GetAtt" in obj) {
3820
+ const getAtt = obj["Fn::GetAtt"];
3821
+ if (Array.isArray(getAtt) && typeof getAtt[0] === "string") {
3822
+ return getAtt[0];
3823
+ }
3824
+ }
3825
+ return void 0;
3826
+ }
3704
3827
  };
3705
3828
 
3706
3829
  // src/analyzer/replacement-rules.ts
@@ -26890,7 +27013,7 @@ function reorderArgs(argv) {
26890
27013
  }
26891
27014
  async function main() {
26892
27015
  const program = new Command8();
26893
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.0.3");
27016
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.0.4");
26894
27017
  program.addCommand(createBootstrapCommand());
26895
27018
  program.addCommand(createSynthCommand());
26896
27019
  program.addCommand(createDeployCommand());