App-FargateStack

 view release on metacpan or  search on metacpan

lib/App/FargateStack/Checker.pm  view on Meta::CPAN


  foreach my $svc (@services) {
    my $result = eval { _simulate_passrole_once( $iam, $policy_source_arn, $role_arns, $svc ) };
    if ( $EVAL_ERROR || !$result ) {
      # If org blocks simulation, warn rather than fail
      return row_warn( 'iam:PassRole', 'Simulation not permitted; verify PassRole manually' );
    }

    # result is an arrayref of EvalDecision strings: allowed | explicitDeny | implicitDeny
    my @dec = @{ $result || [] };
    $decisions_by_service{$svc} = \@dec;
  }

  # Aggregate decisions: any explicitDeny -> FAIL
  # otherwise any implicitDeny -> WARN
  # otherwise all allowed -> PASS
  my $detail = q{};
  my $status = 'PASS';

  foreach my $svc (@services) {
    my $dec         = $decisions_by_service{$svc} || [];
    my $svc_summary = sprintf '%s: %s', $svc, ( join q{,}, @{$dec} );

    if ( grep { $_ eq 'explicitDeny' } @{$dec} ) {
      $status = 'FAIL';
    }
    elsif ( $status ne 'FAIL' && grep { $_ eq 'implicitDeny' } @{$dec} ) {
      $status = 'WARN';
    }

    $detail .= $svc_summary . q{ };
  }

  $detail =~ s/\s+\z//;

  if ( $status eq 'FAIL' ) { return row_fail( 'iam:PassRole', $detail ); }
  if ( $status eq 'WARN' ) { return row_warn( 'iam:PassRole', $detail ); }
  return row_ok( 'iam:PassRole', $detail || 'allowed' );
}

########################################################################
sub _principal_to_policy_source_arn {
########################################################################
  my ( $sts_arn, $account_id ) = @_;

  # Examples:
  # arn:aws:sts::123456789012:assumed-role/DeployerRole/SESSION -> arn:aws:iam::123456789012:role/DeployerRole
  # arn:aws:iam::123456789012:user/someuser                     -> arn:aws:iam::123456789012:user/someuser

  return q{} if !$sts_arn || !$account_id;

  if ( $sts_arn =~ m{\A arn:aws:sts::\Q$account_id\E:assumed-role/([^/]+)/}xsm ) {
    my $role = $1;
    return sprintf 'arn:aws:iam::%s:role/%s', $account_id, $role;
  }

  if ( $sts_arn =~ m{\A arn:aws:iam::\Q$account_id\E:(user|role)/}xsm ) {
    return $sts_arn;
  }

  # Fallback: try to coerce to iam::role if it looks like an sts assumed role
  return $sts_arn;
}

########################################################################
sub _role_arns_from_names {
########################################################################
  my ( $account_id, $role_names ) = @_;

  $role_names ||= [];
  my @arns;

  foreach my $name ( @{$role_names} ) {
    next if !$name;
    push @arns, sprintf 'arn:aws:iam::%s:role/%s', $account_id, $name;
  }

  return \@arns;
}

########################################################################
sub _simulate_passrole_once {
########################################################################
  my ( $iam, $policy_source_arn, $resource_arns, $passed_to_service ) = @_;

  # Build CLI JSON for --context-entries
  my $context_json = sprintf
    q|[{"ContextKeyName":"iam:PassedToService","ContextKeyValues":["%s"],"ContextKeyType":"string"}]|,
    $passed_to_service;

  # Ask for just the decision list
  return $iam->command(
    'simulate-principal-policy' => [
      '--policy-source-arn' => $policy_source_arn,
      '--action-names'      => 'iam:PassRole',
      '--resource-arns'     => @{$resource_arns},
      '--context-entries'   => $context_json,
      '--query'             => 'EvaluationResults[].EvalDecision',
      '--output'            => 'json',
    ]
  );
}

########################################################################
sub _gate {
########################################################################
  my ( $need, $caps ) = @_;

  my $result = 'YES';

  foreach ( @{$need} ) {
    next        if $caps->{$_} =~ /PASS/xsm;
    return 'NO' if $caps->{$_} eq 'FAIL';

    $result = 'MAYBE';
  }

  return $result;
}

########################################################################



( run in 0.544 second using v1.01-cache-2.11-cpan-39bf76dae61 )