---
policy: /ce/ca/aws/ecs/service-public-ip-auto-assignment
logic: /ce/ca/aws/ecs/service-public-ip-auto-assignment/prod.logic.yaml
executionTime: 2025-12-17T01:30:46.206887435Z
generationMs: 55
executionMs: 614
rows:
  - id: test1
    match: true
    status:
      expected: DISAPPEARED
      actual: DISAPPEARED
    conditionIndex:
      expected: 99
      actual: 99
    conditionText:
      expected: isDisappeared(CA10__disappearanceTime__c)
      actual: isDisappeared(CA10__disappearanceTime__c)
    runtimeError: {}
  - id: test2
    match: true
    status:
      expected: INCOMPLIANT
      actual: INCOMPLIANT
    conditionIndex:
      expected: 299
      actual: 299
    conditionText:
      expected: extract('CA10__assignPublicIp__c') == 'Enabled'
      actual: extract('CA10__assignPublicIp__c') == 'Enabled'
    runtimeError: {}
  - id: test3
    match: true
    status:
      expected: COMPLIANT
      actual: COMPLIANT
    conditionIndex:
      expected: 399
      actual: 399
    conditionText:
      expected: extract('CA10__assignPublicIp__c') == 'Disabled'
      actual: extract('CA10__assignPublicIp__c') == 'Disabled'
    runtimeError: {}
  - id: test4
    match: true
    status:
      expected: INAPPLICABLE
      actual: INAPPLICABLE
    conditionIndex:
      expected: 199
      actual: 199
    conditionText:
      expected: extract('CA10__status__c') != 'ACTIVE'
      actual: extract('CA10__status__c') != 'ACTIVE'
    runtimeError: {}
usedFiles:
  - path: /ce/ca/aws/ecs/service-public-ip-auto-assignment/policy.yaml
    md5Hash: 40E908F9A08D4DE1E151C0B348245B1D
    content: |
      ---
      names:
        full: "AWS ECS Service automatically assigns public IP addresses"
        contextual: "Service automatically assigns public IP addresses"
      description: >
        Ensure that Amazon ECS services do not automatically assign public IP addresses
        to tasks. Assigning public IPs to tasks makes them directly accessible from
        the internet, increasing the attack surface.
      type: "COMPLIANCE_POLICY"
      categories:
        - "SECURITY"
      frameworkMappings:
        - "/frameworks/cloudaware/resource-security/public-and-anonymous-access"
        - "/frameworks/aws-fsbp-v1.0.0/ecs/02"
      similarPolicies:
        awsSecurityHub:
          - name: "[ECS.2] ECS services should not have public IP addresses assigned to them automatically"
            url: "https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-2"
  - path: /ce/ca/aws/ecs/service-public-ip-auto-assignment/prod.logic.yaml
    md5Hash: FF679D476AFCDD5849E22664756F6415
    content: |
      ---
      inputType: "CA10__CaAwsEcsService__c"
      testData:
        - file: "test-data.json"
      importExtracts:
        - file: "/types/CA10__CaAwsEcsService__c/object.extracts.yaml"
      conditions:
        - status: "INAPPLICABLE"
          currentStateMessage: "The Service is not active."
          check:
            NOT_EQUAL:
              left:
                EXTRACT: "CA10__status__c"
              right:
                TEXT: "ACTIVE"
        - status: "INCOMPLIANT"
          currentStateMessage: "The ECS Service is configured to automatically assign public IP addresses."
          remediationMessage: "Update the Service network configuration to disable Auto-assign public IP."
          check:
            IS_EQUAL:
              left:
                EXTRACT: "CA10__assignPublicIp__c"
              right:
                TEXT: "Enabled"
        - status: "COMPLIANT"
          currentStateMessage: "The ECS Service is configured to not assign public IP addresses."
          check:
            IS_EQUAL:
              left:
                EXTRACT: "CA10__assignPublicIp__c"
              right:
                TEXT: "Disabled"
      otherwise:
        status: "UNDETERMINED"
        currentStateMessage: "Unexpected values in the field."
  - path: /ce/ca/aws/ecs/service-public-ip-auto-assignment/test-data.json
    md5Hash: CB1855150DDA59389504020AF251CD34
    content: |-
      [
          {
              "CA10__assignPublicIp__c": "Disabled",
              "expectedResult": {
                  "runtimeError": null,
                  "conditionText": "isDisappeared(CA10__disappearanceTime__c)",
                  "conditionIndex": 99,
                  "status": "DISAPPEARED"
              },
              "context": {
                  "snapshotTime": "2025-12-12T04:42:59Z"
              },
              "Id": "test1",
              "CA10__disappearanceTime__c": "2025-12-05T02:07:04Z",
              "CA10__status__c": "ACTIVE"
          },
          {
              "CA10__assignPublicIp__c": "Enabled",
              "expectedResult": {
                  "runtimeError": null,
                  "conditionText": "extract('CA10__assignPublicIp__c') == 'Enabled'",
                  "conditionIndex": 299,
                  "status": "INCOMPLIANT"
              },
              "context": {
                  "snapshotTime": "2025-12-12T04:42:59Z"
              },
              "Id": "test2",
              "CA10__disappearanceTime__c": null,
              "CA10__status__c": "ACTIVE"
          },
          {
              "CA10__assignPublicIp__c": "Disabled",
              "expectedResult": {
                  "runtimeError": null,
                  "conditionText": "extract('CA10__assignPublicIp__c') == 'Disabled'",
                  "conditionIndex": 399,
                  "status": "COMPLIANT"
              },
              "context": {
                  "snapshotTime": "2025-12-12T04:42:59Z"
              },
              "Id": "test3",
              "CA10__disappearanceTime__c": null,
              "CA10__status__c": "ACTIVE"
          },
          {
              "CA10__assignPublicIp__c": "Disabled",
              "expectedResult": {
                  "runtimeError": null,
                  "conditionText": "extract('CA10__status__c') != 'ACTIVE'",
                  "conditionIndex": 199,
                  "status": "INAPPLICABLE"
              },
              "context": {
                  "snapshotTime": "2025-12-12T04:42:59Z"
              },
              "Id": "test4",
              "CA10__disappearanceTime__c": null,
              "CA10__status__c": "INACTIVE"
          }
      ]
  - path: /types/CA10__CaAwsEcsService__c/object.extracts.yaml
    md5Hash: DE9B8176332CECD4A8C53207B8DA95F0
    content: |
      ---
      extracts:
      # Values: Enabled | Disabled. Not nullable.
        - name: "CA10__assignPublicIp__c"
          value:
            FIELD:
              path: "CA10__assignPublicIp__c"
              undeterminedIf:
                noAccessDelegate:
                  path: "CA10__assignPublicIp__c"
                  currentStateMessage: "Service Assign Public IP config cannot be empty. Possible permission issue with ecs:DescribeServices"
      # Values: ACTIVE | DRAINING | INACTIVE. Not nullable.
        - name: "CA10__status__c"
          value:
            FIELD:
              path: "CA10__status__c"
              undeterminedIf:
                noAccessDelegate:
                  path: "CA10__status__c"
                  currentStateMessage: "Service status cannot be empty. Possible permission issue with ecs:DescribeServices"
      # Values: EC2 | FARGATE | EXTERNAL | MANAGED_INSTANCES
      # Nullable
        - name: "CA10__launchType__c"
          value:
            FIELD:
              path: "CA10__launchType__c"
      # Nullable.
        - name: "CA10__platformVersion__c"
          value:
            FIELD:
              path: "CA10__platformVersion__c"
script: |-
  CREATE TEMP FUNCTION mock_ExpectedResult()
  RETURNS ARRAY<STRUCT<
          Id STRING,
          expectedResult STRUCT<status STRING, conditionIndex DECIMAL, conditionText STRING, runtimeError STRING>
      >>
  DETERMINISTIC
  LANGUAGE js
  AS r"""
    return [ {
    "Id" : "test1",
    "expectedResult" : {
      "runtimeError" : null,
      "conditionText" : "isDisappeared(CA10__disappearanceTime__c)",
      "conditionIndex" : 99,
      "status" : "DISAPPEARED"
    }
  }, {
    "Id" : "test2",
    "expectedResult" : {
      "runtimeError" : null,
      "conditionText" : "extract('CA10__assignPublicIp__c') == 'Enabled'",
      "conditionIndex" : 299,
      "status" : "INCOMPLIANT"
    }
  }, {
    "Id" : "test3",
    "expectedResult" : {
      "runtimeError" : null,
      "conditionText" : "extract('CA10__assignPublicIp__c') == 'Disabled'",
      "conditionIndex" : 399,
      "status" : "COMPLIANT"
    }
  }, {
    "Id" : "test4",
    "expectedResult" : {
      "runtimeError" : null,
      "conditionText" : "extract('CA10__status__c') != 'ACTIVE'",
      "conditionIndex" : 199,
      "status" : "INAPPLICABLE"
    }
  } ];
  """;

  CREATE TEMP FUNCTION mock_CA10__CaAwsEcsService__c()
  RETURNS ARRAY<STRUCT<
      CA10__disappearanceTime__c TIMESTAMP,
      CA10__status__c STRING,
      CA10__assignPublicIp__c STRING,
      Id STRING,
      context STRUCT<
          snapshotTime TIMESTAMP
      >
  >>
  DETERMINISTIC
  LANGUAGE js
  AS r"""
    return [ {
    "context" : {
      "snapshotTime" : new Date("2025-12-12T04:42:59Z")
    },
    "CA10__disappearanceTime__c" : new Date("2025-12-05T02:07:04Z"),
    "CA10__status__c" : "ACTIVE",
    "CA10__assignPublicIp__c" : "Disabled",
    "Id" : "test1"
  }, {
    "context" : {
      "snapshotTime" : new Date("2025-12-12T04:42:59Z")
    },
    "CA10__status__c" : "ACTIVE",
    "CA10__assignPublicIp__c" : "Enabled",
    "Id" : "test2"
  }, {
    "context" : {
      "snapshotTime" : new Date("2025-12-12T04:42:59Z")
    },
    "CA10__status__c" : "ACTIVE",
    "CA10__assignPublicIp__c" : "Disabled",
    "Id" : "test3"
  }, {
    "context" : {
      "snapshotTime" : new Date("2025-12-12T04:42:59Z")
    },
    "CA10__status__c" : "INACTIVE",
    "CA10__assignPublicIp__c" : "Disabled",
    "Id" : "test4"
  } ];
  """;

  CREATE TEMP FUNCTION process_CA10__CaAwsEcsService__c(
      obj STRUCT<
          CA10__disappearanceTime__c TIMESTAMP,
          CA10__status__c STRING,
          CA10__assignPublicIp__c STRING,
          Id STRING
      >,
      snapshotTime TIMESTAMP
  )
  RETURNS STRUCT<status STRING, conditionIndex DECIMAL, conditionText STRING, currentStateMessage STRING, currentStateReferences STRING, remediation STRING, runtimeError STRING>
  DETERMINISTIC
  LANGUAGE js
  AS r"""
  var TextLib = new function () {
      this.normalize = function(arg) {
          return arg == null ? '' : arg.replace(/\s+/g, ' ').trim().toLowerCase();
      };
      this.isEmpty = function(arg) {
          return this.normalize(arg) == '';
      };
      this.isNotEmpty = function(arg) {
          return this.normalize(arg) != '';
      };
      this.equal = function(left, right) {
          return this.normalize(left) == this.normalize(right);
      };
      this.notEqual = function(left, right) {
          return this.normalize(left) != this.normalize(right);
      };
      this.startsWith = function(arg, substring) {
          return this.normalize(arg).startsWith(this.normalize(substring));
      };
      this.endsWith = function(arg, substring) {
          return this.normalize(arg).endsWith(this.normalize(substring));
      };
      this.contains = function(arg, substring) {
          return this.normalize(arg).includes(this.normalize(substring));
      };
      this.containsAll = function(arg, substrings) {
          if (substrings == null || substrings.length === 0) return false;
          let normalizedArg = this.normalize(arg);
          return substrings.every(sub => normalizedArg.includes(this.normalize(sub)));
      };
      this.containsAny = function(arg, substrings) {
          if (substrings == null || substrings.length === 0) return false;
          let normalizedArg = this.normalize(arg);
          return substrings.some(sub => normalizedArg.includes(this.normalize(sub)));
      };
  }();
      var references1 = [];
      // condition[0], conditionIndex:[0..99]
      references1.push('Deleted From AWS [CA10__disappearanceTime__c]: ' + obj.CA10__disappearanceTime__c);
      if (obj.CA10__disappearanceTime__c != null) {
          return {status: 'DISAPPEARED', conditionIndex: 99, conditionText: "isDisappeared(CA10__disappearanceTime__c)", currentStateMessage: "Object is deleted in the source", currentStateReferences: references1.join('\n'), remediation: null, runtimeError: null};
      }
      // condition[1], conditionIndex:[100..199]
      function fieldChecked4() {
          if (TextLib.isEmpty(obj.CA10__status__c)) {
              throw new Error("UNDETERMINED condition:101", {cause: {status: 'UNDETERMINED', conditionIndex: 101, conditionText: "CA10__status__c.delegatedTo(CA10__status__c).isEmpty()", currentStateMessage: "Service status cannot be empty. Possible permission issue with ecs:DescribeServices", currentStateReferences: references1.join('\n'), remediation: null, runtimeError: null}});
          }
          return obj.CA10__status__c;
      }
      function extract3() { if (!this.out) { this.out = fieldChecked4(); } return this.out; };
      references1.push('Status [obj.CA10__status__c]: ' + obj.CA10__status__c);
      try {
          if (TextLib.notEqual(extract3.call(extract3), 'ACTIVE')) {
              return {status: 'INAPPLICABLE', conditionIndex: 199, conditionText: "extract('CA10__status__c') != 'ACTIVE'", currentStateMessage: "The Service is not active.", currentStateReferences: references1.join('\n'), remediation: null, runtimeError: null};
          }
      } catch (err) {
          if (err.cause && err.cause.status) { return err.cause; } else { throw err; }
      }
      // condition[2], conditionIndex:[200..299]
      function fieldChecked7() {
          if (TextLib.isEmpty(obj.CA10__assignPublicIp__c)) {
              throw new Error("UNDETERMINED condition:201", {cause: {status: 'UNDETERMINED', conditionIndex: 201, conditionText: "CA10__assignPublicIp__c.delegatedTo(CA10__assignPublicIp__c).isEmpty()", currentStateMessage: "Service Assign Public IP config cannot be empty. Possible permission issue with ecs:DescribeServices", currentStateReferences: references1.join('\n'), remediation: null, runtimeError: null}});
          }
          return obj.CA10__assignPublicIp__c;
      }
      function extract6() { if (!this.out) { this.out = fieldChecked7(); } return this.out; };
      references1.push('Assign Public IP [obj.CA10__assignPublicIp__c]: ' + obj.CA10__assignPublicIp__c);
      try {
          if (TextLib.equal(extract6.call(extract6), 'Enabled')) {
              return {status: 'INCOMPLIANT', conditionIndex: 299, conditionText: "extract('CA10__assignPublicIp__c') == 'Enabled'", currentStateMessage: "The ECS Service is configured to automatically assign public IP addresses.", currentStateReferences: references1.join('\n'), remediation: "Update the Service network configuration to disable Auto-assign public IP.", runtimeError: null};
          }
      } catch (err) {
          if (err.cause && err.cause.status) { return err.cause; } else { throw err; }
      }
      // condition[3], conditionIndex:[300..399]
      function fieldChecked10() {
          if (TextLib.isEmpty(obj.CA10__assignPublicIp__c)) {
              throw new Error("UNDETERMINED condition:301", {cause: {status: 'UNDETERMINED', conditionIndex: 301, conditionText: "CA10__assignPublicIp__c.delegatedTo(CA10__assignPublicIp__c).isEmpty()", currentStateMessage: "Service Assign Public IP config cannot be empty. Possible permission issue with ecs:DescribeServices", currentStateReferences: references1.join('\n'), remediation: null, runtimeError: null}});
          }
          return obj.CA10__assignPublicIp__c;
      }
      function extract9() { if (!this.out) { this.out = fieldChecked10(); } return this.out; };
      try {
          if (TextLib.equal(extract9.call(extract9), 'Disabled')) {
              return {status: 'COMPLIANT', conditionIndex: 399, conditionText: "extract('CA10__assignPublicIp__c') == 'Disabled'", currentStateMessage: "The ECS Service is configured to not assign public IP addresses.", currentStateReferences: references1.join('\n'), remediation: null, runtimeError: null};
          }
      } catch (err) {
          if (err.cause && err.cause.status) { return err.cause; } else { throw err; }
      }
      return {status: 'UNDETERMINED', conditionIndex: 400, conditionText: "otherwise", currentStateMessage: "Unexpected values in the field.", currentStateReferences: references1.join('\n'), remediation: null, runtimeError: null};
  """;


  SELECT
    expectedResult.Id as Id,
    IF (
      IFNULL(expectedResult.expectedResult.status, '') = IFNULL(sObject.result.status, '')
      AND IFNULL(expectedResult.expectedResult.conditionIndex, -1) = IFNULL(sObject.result.conditionIndex, -1)
      AND IFNULL(expectedResult.expectedResult.conditionText, '') = IFNULL(sObject.result.conditionText, '')
      AND IFNULL(expectedResult.expectedResult.runtimeError, '') = IFNULL(sObject.result.runtimeError, ''),
      "MATCH",
      "FAIL"
    ) as match,
    expectedResult.expectedResult.status as expectedStatus, sObject.result.status as actualStatus,
    expectedResult.expectedResult.conditionIndex as expectedConditionIndex, sObject.result.conditionIndex as actualConditionIndex,
    expectedResult.expectedResult.conditionText as expectedConditionText, sObject.result.conditionText as actualConditionText,
    expectedResult.expectedResult.runtimeError as expectedRuntimeError, sObject.result.runtimeError as actualRuntimeError
  FROM UNNEST(mock_ExpectedResult()) expectedResult
  LEFT JOIN (
      SELECT
          sObject.CA10__disappearanceTime__c AS CA10__disappearanceTime__c,
          sObject.CA10__status__c AS CA10__status__c,
          sObject.CA10__assignPublicIp__c AS CA10__assignPublicIp__c,
          sObject.Id AS Id,
          process_CA10__CaAwsEcsService__c(
              STRUCT(
                  sObject.CA10__disappearanceTime__c AS CA10__disappearanceTime__c,
                  sObject.CA10__status__c AS CA10__status__c,
                  sObject.CA10__assignPublicIp__c AS CA10__assignPublicIp__c,
                  sObject.Id AS Id
              ),
              sObject.context.snapshotTime
          ) as result
      FROM UNNEST(mock_CA10__CaAwsEcsService__c()) AS sObject
  ) sObject ON sObject.Id = expectedResult.Id;
