Skip to main content

Contract: Policy

What is it & why do I care about it?

The Policy Contract is an essential piece of Event Processing Workflow. Specifically, that workflow uses the Policy to perform a judgement on each new Observation, and, if the judgement is negative, then the workflow needs to open a Violation for the corresponding Resource. This contract defines the input and output of the Policy.

  1. Event Processing Workflow
  2. Observer
  3. Observances
  4. Policies
  5. Resources

Version: denov1

Description

This is the current version of the Policy contract, and new Policies should be written with this version.

Specifically, this contract uses Deno as its Javascript/Typescript runtime.

Input Structure

interface Input {
// A list of the ten most recent observance objects, as output by the corresponding Policy
observances: Array<{
[k: string]: any
}>;
}

Output Structure

type Output = Array<{
violationTypeId: string;
}>

Usage Instructions

Specifically, your denov1-compatible Policy expression MUST contain a function named expression, which accepts the above Input and returns the above Output.

You may notice that the Input observances field is an array. This is because the Policy isn't just given the most recent Observance, but the ten most recent Observances. If your policy is non-temporal, you should probably only look at the most recent Observance, index zero. If your policy is temporal, you can choose to wait for multiple failures in a row before reporting a Violation. This can result in better suppression of transient errors in some Temporal Policies. (This is only acceptable for Temporal policies because they rerun consistently. A non-Temporal policy might have to wait a long time to rerun, and this could inadvertently extend MTTD for actual Violations.)

You may notice that the Output is an Array. Each Policy may check for multiple types of violation, and return them separately. If there are no violations, return an empty array. For example, your Policy could check for both "backups enabled" and "backup retention period too short", and return different violationTypeIds for each, which would enable them to be surfaced in separate Violations, and suppressed separately.

We execute the lambda in a Deno sandbox with almost no permissions enabled - just some restricted disk access for the runtime that we wrap your provided code with. You don't have network access, general disk access, environment access, etc. Your Policy code should be pure, and therefore shouldn't use these things anyway. (although nothing strictly enforces purity, it could make your Policy's users confused at its inconsistent output, so, it's not recommended.)

A good conceptualization of the boundary between Observer and Policy is that Observers gather data, and Policies judge data. If you find that you want to perform a network request in a Policy, you probably did not adequately gather all the data needed for the judgement in the gathering step, aka, in the Observer.

Error Handling

Your function does not need to implement error handling, and may simply throw exceptions. However, doing this will suppress violations for the resource that an exception was thrown for (because an issue with a Policy isn't an issue for the downstream Resource owner, it's an issue for the Policy's owner)

Sample

Request
{
"observances": [
{
"ServerSideEncryptionRule": {
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "AES256"
}
"BucketKeyEnabled": true
}
}
]
}
Response
[
{
"violationTypeId": "AUTO_BACKUP_NOT_ENABLED"
}
]
Function Body
function expression(input: Input): Output {
const v = input!.observances![0]!.BackupRetentionPeriod;
if (v === undefined || v <= 0) {
return [{ violationTypeId: "AUTO_BACKUP_NOT_ENABLED" }];
}

return [];
}