Last updated Jul 24, 2025
Internal use only

Add New Assessment

Overview

  1. Define a control key, inventory, evidence, evaluation, and output for the assessment.
  2. Implement the assessment in the codebase, including SQL queries, rules, triggers, and actions.
  3. Ensure asset class has a corresponding component in EPM (responsibility-model) with matching inventory.
  4. Test the assessment in a development environment.

Requirements

In general, the following information is needed to add a new assessment:

  • Control key: a unique identifier for the assessment (e.g. SNS-ENCRYPTION-FR)
    • Generally, this is a short string with the asset class (i.e. SNS), the assessment (i.e. ENCRYPTION). The -FR suffix indicates that this is a FedRAMP assessment.
  • Inventory: what set of component instances are we evaluating? (what table, which fields)
  • Evidence: what table(s), which fields
  • Evaluation: what’s the rule, what logic do we run on the inventory + evidence
  • Output:
    • Asset class / EPM Component type
    • Assed ID format / EPM component instance ID/ARN to map to
    • Requirement and Responsibility to map to in EPM

Example:

In general, the evaluations for assessments were defined in this spreadsheet.

Assessment Definitions

For more complex assessments, it may be valuable to write an assessment definition following this template.

Example assessment definition.

Implementation of a New Assessment

Example directory structure for a new assessment:

1
2
src/controls/epm/rdsEncryption/
├── __tests__
│   └── rdsEncryptedRule.test.ts
├── actions
│   └── rdsEncryptedAction.ts
├── control.ts
├── index.ts
├── queries
│   └── batch.sql
└── rules
    └── rdsEncryptedRule.ts

Notes:

  • actions - Intervention actions resulting from failed assessment (i.e. Slack message, Jira ticket)
  • queries - SQL queries to run for the assessment
  • rules - Rules that define the logic for the assessment, including the query to run and the expected results
  • control.ts - Control metadata, including the control key, description, and asset class. Triggers are often defined here as well

An example PR for a new assessment can be found here.

Query

Create a SQL query for Socrates vNext that will be used to assess the asset class. The query should return the necessary fields to determine compliance with the control.

Example query:

1
2
WITH rds_dbs AS (
    SELECT case_data.arn, MAX(case_data.hour) AS max_hour
    FROM {{ caseTableName }} AS case_data
    WHERE case_data.service = "RDS" AND case_data.resource = "DBInstance"
    GROUP BY case_data.arn
)

SELECT DISTINCT
    case_data.arn AS asset_id,
    "AWS-RDS" AS asset_class,
    GET_JSON_OBJECT(case_data.tags, "$.micros_service_id") AS service_id,
    CAST(COALESCE(
        GET_JSON_OBJECT(case_data.configuration, "$.storageEncrypted"),
        GET_JSON_OBJECT(case_data.configuration, "$.StorageEncrypted"),
        GET_JSON_OBJECT(case_data.configuration, "$.encrypted"),
        GET_JSON_OBJECT(case_data.configuration, "$.IsEncrypted"),
        FALSE
    ) AS BOOLEAN) AS is_encrypted
FROM {{ caseTableName }} AS case_data
INNER JOIN rds_dbs ON case_data.arn = rds_dbs.arn AND case_data.hour = rds_dbs.max_hour
INNER JOIN {{ awsAccountsTableName }} AS aa ON case_data.awsaccountid = aa.id;

Note: Field names from this query are asset_id, asset_class, service_id, and is_encrypted. The {{ caseTableName }} and {{ awsAccountsTableName }} are placeholders that will be replaced by the actual table names when the query is executed.

Trigger

Create a trigger that runs the SQL query on a schedule. The trigger can be defined in a class that extends TemplatedBatchTrigger to take advantage of a templated query. A templated query is particularly useful when running the same query against different table names such as those found in-boundary. The trigger will use the SQL query defined at the given path and substitute the supplied variables.

Example trigger for daily RDS encryption assessment:

1
2
class RdsEncryptedTrigger extends TemplatedBatchTrigger {
    constructor(tableName: string, awsAccountTableName: string) {
        super({
            sqlPath: 'controls/epm/rdsEncryption/queries/batch.sql',
            replacements: {
                caseTableName: tableName,
                awsAccountsTableName: awsAccountTableName,
            },
        });
    }

    metadata(): BatchMetadata {
        return new BatchMetadata('rds_encrypted_batch_trigger', TriggerFrequency.DAILY);
    }

    queryEvidenceMapping(): Record<string, EvidenceFieldDef> {
        return {
            asset_id: new EvidenceFieldDef('asset_id', String, false),
            asset_class: new EvidenceFieldDef('asset_class', String, false),
            service_id: new EvidenceFieldDef('service_id', String, true),
            is_encrypted: new EvidenceFieldDef('is_encrypted', Boolean, false),
        };
    }
}

Note: The queryEvidenceMapping defines the fields from the query to use as evidence.

Actions

Create an action that will be triggered when the assessment fails. This action can send a Slack message or create a Jira ticket, depending on your requirements.

Example action for sending a Slack notification:

1
2
export class RdsEncryptedAction implements ActionRequest {
    get metadata(): ActionRequestMetadata {
        return new ActionRequestMetadata(
            'rds_encrypted_recommendation',
            'Slack notification which alerts service owners to ensure their AWS RDS encryption at rest is enabled',
        );
    }

    shouldRaise(outcomes: RuleOutcomes) {
        return new EQ(outcomes.of('rds_encrypted'), false);
    }

    action(evidence: Record<string, unknown>): SlackAction {
        const assetId = evidence.asset_id as string;
        const serviceId = evidence.service_id as string;
        const message = `Enterprise Posture Management has detected a RDS asset ${assetId} is not encrypted at rest. Please review the actionable items under the "Protect confidentiality and integrity of data at rest" Criteria in the Enterprise Posture Management Dashboard.`;
        let url = 'https://resp-model-ui-public.us-east-1.prod.public.atl-paas.net/components/';

        if ((serviceId || '').trim()) {
            url = `https://resp-model-ui-public.us-east-1.prod.public.atl-paas.net/components/${serviceId}/d21a4e50-26fc-4521-925b-875cbe78fa4e`;
        }

        return new SlackAction(message, url, null);
    }
}

Rules

Create a rule that defines the logic for the assessment. Rules use evidence fields to determine an outcome for an assessment.

Example rule to check if RDS encryption is enabled:

1
2
export class RdsEncryptedRule extends Rule {
    evaluate(evidence: Evidence): RuleOutcome {
        const encrypted = evidence.getField('is_encrypted');
        const expression = new EQ(encrypted, true);

        return new RuleOutcome('rds_encrypted', 'Checks whether the AWS RDS resource has encryption enabled', expression);
    }
}

Control

Create a control that ties everything together. This control will define the control key, description, and the action to take when the assessment fails.

1
2
export const RdsEncryption = buildEpmControl({
    active: true,
    owner: 'jdoe',
    key: 'RDS-DB-ENCRYPTION',
    tags: ['rds', 'standards', 'encryption'],
    supportChannel: new SupportChannel(
        '#help-enterprise-posture-management',
        'https://atlassian.enterprise.slack.com/archives/C02H23XU943',
    ),
    rules: [new RdsEncryptedRule()],
    trigger: new RdsEncryptedTrigger(COMMERCIAL_CASE_TABLE_NAME, COMMERCIAL_AWS_ACCOUNTS_TABLE_NAME),
    actions: [new RdsEncryptedAction()],
    perimeters: [MicrosPerimeter.COMMERCIAL],
    name: null,
    enrichments: [],
    applicabilityRule: null,
    businessUnit: null,
    opsgenieNotifications: null,
});

Testing a New Assessment

Since assessments are only run once daily, it may be valuable to manually test an assessment via the REST API. Call /api/v1/evidence/collect/<CONTROL_KEY>?limit=-1 with the control key to trigger assessments for a specific control.

In-Boundary Assessments Ensure asset class is supported for egress (if in-boundary)

Transit Policy

All in-boundary assessment results are subject to the transit policy. The change in this PR will obfuscate ARNs for all asset classes starting with AWS, Aws, or aws. This means any other asset classes will be dropped from transit out of boundary.

Rate this page: