Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(obligatron): Augment tagging results with AMI info #1105

Merged
merged 1 commit into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
GRANT SELECT ON public.aws_ec2_images TO obligatron;
1 change: 0 additions & 1 deletion packages/common/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ model aws_ec2_images {
virtualization_type String?

@@id([account_id, region, arn], map: "aws_ec2_images_cqpk")
@@ignore
}

model aws_ec2_instances {
Expand Down
28 changes: 20 additions & 8 deletions packages/obligatron/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import type { PrismaClient } from '@prisma/client';
import { getPrismaClient } from 'common/database';
import { config } from 'dotenv';
import { getConfig } from './config';
import type { ObligationResult } from './obligations';
import { evaluateTaggingObligation } from './obligations/tagging';
import {
evaluateAmiTaggingCoverage,
evaluateSecurityHubTaggingCoverage,
} from './obligations/tagging';

config({ path: `../../.env` }); // Load `.env` file at the root of the repository

Expand All @@ -12,6 +16,20 @@ const stringIsObligation = (input: string): input is Obligation => {
return Obligations.filter((v) => v === input).length > 0;
};

async function getResults(
obligation: Obligation,
db: PrismaClient,
): Promise<ObligationResult[]> {
switch (obligation) {
case 'TAGGING': {
return [
...(await evaluateSecurityHubTaggingCoverage(db)),
...(await evaluateAmiTaggingCoverage(db)),
];
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should there be a default case here, maybe with a console log statement?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, there should. The linter should also pick this up. Given this isn't a change from before, I'll address this in a separate PR, solving it via lint config.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I'm inclined to say that there shouldn't be a default and that the linter should be highlighting that there are branches that aren't handled (if we had more than 1 obligation...), but maybe Typescript doesn't do that.

Copy link
Member Author

@akash1810 akash1810 Jun 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upon closer inspection, I think typescript is being clever here - we're switching on a variable of type Obligation. Currently this has only one value, therefore typescript is happy with the lack of a default. Indeed, adding an obligation creates a compile error:

image

That is, nothing to do here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a linting rule for switch exhaustiveness which may or may not be useful here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the most part this repository is using the standard departmental configuration. Will see about adding this upstream.

TIL --print-config shows what rules are being evaluated by eslint.

npx eslint --print-config packages/obligatron/src/index.ts

Yields:

{
  "env": {
    "browser": true,
    "es6": true,
    "node": true
  },
  "globals": {},
  "parser": "/Users/akash_askoolum/code/service-catalogue/node_modules/@typescript-eslint/parser/dist/index.js",
  "parserOptions": {
    "ecmaVersion": "es2020",
    "project": "./tsconfig.json",
    "sourceType": "module"
  },
  "plugins": [
    "import",
    "eslint-comments",
    "@typescript-eslint",
    "unicorn",
    "prettier"
  ],
  "rules": {
    "prettier/prettier": [
      "error"
    ],
    "@typescript-eslint/no-non-null-assertion": [
      "error"
    ],
    "unicorn/prefer-array-flat-map": [
      "error"
    ],
    "@typescript-eslint/array-type": [
      2,
      {
        "default": "array-simple"
      }
    ],
    "@typescript-eslint/consistent-indexed-object-style": [
      2,
      "record"
    ],
    "@typescript-eslint/consistent-type-imports": [
      2,
      {
        "prefer": "type-imports"
      }
    ],
    "@typescript-eslint/member-delimiter-style": [
      2,
      {
        "multiline": {
          "delimiter": "semi",
          "requireLast": true
        },
        "singleline": {
          "delimiter": "semi",
          "requireLast": false
        }
      }
    ],
    "@typescript-eslint/no-confusing-non-null-assertion": [
      2
    ],
    "@typescript-eslint/no-unnecessary-boolean-literal-compare": [
      2
    ],
    "@typescript-eslint/no-unnecessary-condition": [
      2
    ],
    "@typescript-eslint/no-unnecessary-qualifier": [
      2
    ],
    "@typescript-eslint/no-unnecessary-type-arguments": [
      2
    ],
    "@typescript-eslint/prefer-includes": [
      2
    ],
    "@typescript-eslint/prefer-reduce-type-parameter": [
      2
    ],
    "@typescript-eslint/prefer-string-starts-ends-with": [
      2
    ],
    "@typescript-eslint/prefer-ts-expect-error": [
      2
    ],
    "@typescript-eslint/naming-convention": [
      2,
      {
        "selector": [
          "typeLike",
          "enumMember"
        ],
        "format": [
          "PascalCase"
        ]
      }
    ],
    "@typescript-eslint/prefer-nullish-coalescing": [
      2
    ],
    "@typescript-eslint/prefer-optional-chain": [
      2
    ],
    "@typescript-eslint/no-explicit-any": [
      2
    ],
    "import/namespace": [
      0
    ],
    "import/named": [
      "off"
    ],
    "@typescript-eslint/await-thenable": [
      "error"
    ],
    "@typescript-eslint/ban-ts-comment": [
      "error"
    ],
    "@typescript-eslint/ban-types": [
      "error"
    ],
    "no-array-constructor": [
      "off"
    ],
    "@typescript-eslint/no-array-constructor": [
      "error"
    ],
    "@typescript-eslint/no-base-to-string": [
      "error"
    ],
    "@typescript-eslint/no-duplicate-enum-values": [
      "error"
    ],
    "@typescript-eslint/no-duplicate-type-constituents": [
      "error"
    ],
    "@typescript-eslint/no-extra-non-null-assertion": [
      "error"
    ],
    "@typescript-eslint/no-floating-promises": [
      "error"
    ],
    "@typescript-eslint/no-for-in-array": [
      "error"
    ],
    "no-implied-eval": [
      "off"
    ],
    "@typescript-eslint/no-implied-eval": [
      "error"
    ],
    "no-loss-of-precision": [
      "off"
    ],
    "@typescript-eslint/no-loss-of-precision": [
      "error"
    ],
    "@typescript-eslint/no-misused-new": [
      "error"
    ],
    "@typescript-eslint/no-misused-promises": [
      "error"
    ],
    "@typescript-eslint/no-namespace": [
      "error"
    ],
    "@typescript-eslint/no-non-null-asserted-optional-chain": [
      "error"
    ],
    "@typescript-eslint/no-redundant-type-constituents": [
      "error"
    ],
    "@typescript-eslint/no-this-alias": [
      "error"
    ],
    "@typescript-eslint/no-unnecessary-type-assertion": [
      "error"
    ],
    "@typescript-eslint/no-unnecessary-type-constraint": [
      "error"
    ],
    "@typescript-eslint/no-unsafe-argument": [
      "error"
    ],
    "@typescript-eslint/no-unsafe-assignment": [
      "error"
    ],
    "@typescript-eslint/no-unsafe-call": [
      "error"
    ],
    "@typescript-eslint/no-unsafe-declaration-merging": [
      "error"
    ],
    "@typescript-eslint/no-unsafe-enum-comparison": [
      "error"
    ],
    "@typescript-eslint/no-unsafe-member-access": [
      "error"
    ],
    "@typescript-eslint/no-unsafe-return": [
      "error"
    ],
    "no-unused-vars": [
      "off"
    ],
    "@typescript-eslint/no-unused-vars": [
      "error"
    ],
    "@typescript-eslint/no-var-requires": [
      "error"
    ],
    "@typescript-eslint/prefer-as-const": [
      "error"
    ],
    "require-await": [
      "off"
    ],
    "@typescript-eslint/require-await": [
      "error"
    ],
    "@typescript-eslint/restrict-plus-operands": [
      "error"
    ],
    "@typescript-eslint/restrict-template-expressions": [
      "error"
    ],
    "@typescript-eslint/triple-slash-reference": [
      "error"
    ],
    "@typescript-eslint/unbound-method": [
      "error"
    ],
    "constructor-super": [
      "off"
    ],
    "getter-return": [
      "off"
    ],
    "no-const-assign": [
      "off"
    ],
    "no-dupe-args": [
      "off"
    ],
    "no-dupe-class-members": [
      "off"
    ],
    "no-dupe-keys": [
      "off"
    ],
    "no-func-assign": [
      "off"
    ],
    "no-import-assign": [
      "off"
    ],
    "no-new-symbol": [
      "off"
    ],
    "no-obj-calls": [
      "off"
    ],
    "no-redeclare": [
      "off"
    ],
    "no-setter-return": [
      "off"
    ],
    "no-this-before-super": [
      "off"
    ],
    "no-undef": [
      "off"
    ],
    "no-unreachable": [
      "off"
    ],
    "no-unsafe-negation": [
      "off"
    ],
    "no-var": [
      "error"
    ],
    "prefer-const": [
      "error"
    ],
    "prefer-rest-params": [
      "error"
    ],
    "prefer-spread": [
      "error"
    ],
    "curly": [
      "error",
      "multi-line"
    ],
    "eslint-comments/disable-enable-pair": [
      2,
      {
        "allowWholeFile": true
      }
    ],
    "eslint-comments/no-duplicate-disable": [
      2
    ],
    "eslint-comments/no-unused-disable": [
      2
    ],
    "eslint-comments/no-unused-enable": [
      2
    ],
    "eslint-comments/require-description": [
      2,
      {
        "ignore": [
          "eslint-enable"
        ]
      }
    ],
    "no-underscore-dangle": [
      0
    ],
    "import/no-default-export": [
      2
    ],
    "import/prefer-default-export": [
      0
    ],
    "import/first": [
      2
    ],
    "sort-imports": [
      "error",
      {
        "ignoreCase": true,
        "ignoreDeclarationSort": true,
        "ignoreMemberSort": false,
        "allowSeparatedGroups": false
      }
    ],
    "import/newline-after-import": [
      2
    ],
    "import/order": [
      "error",
      {
        "groups": [
          "builtin",
          "external",
          "internal",
          "parent",
          "sibling",
          "index",
          "object",
          "unknown"
        ],
        "newlines-between": "never",
        "alphabetize": {
          "order": "asc",
          "caseInsensitive": true,
          "orderImportKind": "ignore"
        },
        "distinctGroup": true,
        "warnOnUnassignedImports": false
      }
    ],
    "import/no-cycle": [
      "error",
      {
        "ignoreExternal": true,
        "allowUnsafeDynamicCyclicDependency": false
      }
    ],
    "no-unexpected-multiline": [
      0
    ],
    "@typescript-eslint/lines-around-comment": [
      0
    ],
    "@typescript-eslint/quotes": [
      0
    ],
    "babel/quotes": [
      0
    ],
    "unicorn/template-indent": [
      0
    ],
    "vue/html-self-closing": [
      0
    ],
    "vue/max-len": [
      0
    ],
    "@babel/object-curly-spacing": [
      "off"
    ],
    "@babel/semi": [
      "off"
    ],
    "@typescript-eslint/block-spacing": [
      "off"
    ],
    "@typescript-eslint/brace-style": [
      "off"
    ],
    "@typescript-eslint/comma-dangle": [
      "off"
    ],
    "@typescript-eslint/comma-spacing": [
      "off"
    ],
    "@typescript-eslint/func-call-spacing": [
      "off"
    ],
    "@typescript-eslint/indent": [
      "off"
    ],
    "@typescript-eslint/key-spacing": [
      "off"
    ],
    "@typescript-eslint/keyword-spacing": [
      "off"
    ],
    "@typescript-eslint/no-extra-parens": [
      "off"
    ],
    "@typescript-eslint/no-extra-semi": [
      "off"
    ],
    "@typescript-eslint/object-curly-spacing": [
      "off"
    ],
    "@typescript-eslint/semi": [
      "off"
    ],
    "@typescript-eslint/space-before-blocks": [
      "off"
    ],
    "@typescript-eslint/space-before-function-paren": [
      "off"
    ],
    "@typescript-eslint/space-infix-ops": [
      "off"
    ],
    "@typescript-eslint/type-annotation-spacing": [
      "off"
    ],
    "babel/object-curly-spacing": [
      "off"
    ],
    "babel/semi": [
      "off"
    ],
    "flowtype/boolean-style": [
      "off"
    ],
    "flowtype/delimiter-dangle": [
      "off"
    ],
    "flowtype/generic-spacing": [
      "off"
    ],
    "flowtype/object-type-curly-spacing": [
      "off"
    ],
    "flowtype/object-type-delimiter": [
      "off"
    ],
    "flowtype/quotes": [
      "off"
    ],
    "flowtype/semi": [
      "off"
    ],
    "flowtype/space-after-type-colon": [
      "off"
    ],
    "flowtype/space-before-generic-bracket": [
      "off"
    ],
    "flowtype/space-before-type-colon": [
      "off"
    ],
    "flowtype/union-intersection-spacing": [
      "off"
    ],
    "react/jsx-child-element-spacing": [
      "off"
    ],
    "react/jsx-closing-bracket-location": [
      "off"
    ],
    "react/jsx-closing-tag-location": [
      "off"
    ],
    "react/jsx-curly-newline": [
      "off"
    ],
    "react/jsx-curly-spacing": [
      "off"
    ],
    "react/jsx-equals-spacing": [
      "off"
    ],
    "react/jsx-first-prop-new-line": [
      "off"
    ],
    "react/jsx-indent": [
      "off"
    ],
    "react/jsx-indent-props": [
      "off"
    ],
    "react/jsx-max-props-per-line": [
      "off"
    ],
    "react/jsx-newline": [
      "off"
    ],
    "react/jsx-one-expression-per-line": [
      "off"
    ],
    "react/jsx-props-no-multi-spaces": [
      "off"
    ],
    "react/jsx-tag-spacing": [
      "off"
    ],
    "react/jsx-wrap-multilines": [
      "off"
    ],
    "standard/array-bracket-even-spacing": [
      "off"
    ],
    "standard/computed-property-even-spacing": [
      "off"
    ],
    "standard/object-curly-even-spacing": [
      "off"
    ],
    "unicorn/empty-brace-spaces": [
      "off"
    ],
    "unicorn/no-nested-ternary": [
      "off"
    ],
    "unicorn/number-literal-case": [
      "off"
    ],
    "vue/array-bracket-newline": [
      "off"
    ],
    "vue/array-bracket-spacing": [
      "off"
    ],
    "vue/array-element-newline": [
      "off"
    ],
    "vue/arrow-spacing": [
      "off"
    ],
    "vue/block-spacing": [
      "off"
    ],
    "vue/block-tag-newline": [
      "off"
    ],
    "vue/brace-style": [
      "off"
    ],
    "vue/comma-dangle": [
      "off"
    ],
    "vue/comma-spacing": [
      "off"
    ],
    "vue/comma-style": [
      "off"
    ],
    "vue/dot-location": [
      "off"
    ],
    "vue/func-call-spacing": [
      "off"
    ],
    "vue/html-closing-bracket-newline": [
      "off"
    ],
    "vue/html-closing-bracket-spacing": [
      "off"
    ],
    "vue/html-end-tags": [
      "off"
    ],
    "vue/html-indent": [
      "off"
    ],
    "vue/html-quotes": [
      "off"
    ],
    "vue/key-spacing": [
      "off"
    ],
    "vue/keyword-spacing": [
      "off"
    ],
    "vue/max-attributes-per-line": [
      "off"
    ],
    "vue/multiline-html-element-content-newline": [
      "off"
    ],
    "vue/multiline-ternary": [
      "off"
    ],
    "vue/mustache-interpolation-spacing": [
      "off"
    ],
    "vue/no-extra-parens": [
      "off"
    ],
    "vue/no-multi-spaces": [
      "off"
    ],
    "vue/no-spaces-around-equal-signs-in-attribute": [
      "off"
    ],
    "vue/object-curly-newline": [
      "off"
    ],
    "vue/object-curly-spacing": [
      "off"
    ],
    "vue/object-property-newline": [
      "off"
    ],
    "vue/operator-linebreak": [
      "off"
    ],
    "vue/quote-props": [
      "off"
    ],
    "vue/script-indent": [
      "off"
    ],
    "vue/singleline-html-element-content-newline": [
      "off"
    ],
    "vue/space-in-parens": [
      "off"
    ],
    "vue/space-infix-ops": [
      "off"
    ],
    "vue/space-unary-ops": [
      "off"
    ],
    "vue/template-curly-spacing": [
      "off"
    ],
    "space-unary-word-ops": [
      "off"
    ],
    "generator-star": [
      "off"
    ],
    "no-comma-dangle": [
      "off"
    ],
    "no-reserved-keys": [
      "off"
    ],
    "no-space-before-semi": [
      "off"
    ],
    "no-wrap-func": [
      "off"
    ],
    "space-after-function-name": [
      "off"
    ],
    "space-before-function-parentheses": [
      "off"
    ],
    "space-in-brackets": [
      "off"
    ],
    "no-arrow-condition": [
      "off"
    ],
    "space-after-keywords": [
      "off"
    ],
    "space-before-keywords": [
      "off"
    ],
    "space-return-throw-case": [
      "off"
    ],
    "no-spaced-func": [
      "off"
    ],
    "indent-legacy": [
      "off"
    ],
    "array-bracket-newline": [
      "off"
    ],
    "array-bracket-spacing": [
      "off"
    ],
    "array-element-newline": [
      "off"
    ],
    "arrow-parens": [
      "off"
    ],
    "arrow-spacing": [
      "off"
    ],
    "block-spacing": [
      "off"
    ],
    "brace-style": [
      "off"
    ],
    "comma-dangle": [
      "off"
    ],
    "comma-spacing": [
      "off"
    ],
    "comma-style": [
      "off"
    ],
    "computed-property-spacing": [
      "off"
    ],
    "dot-location": [
      "off"
    ],
    "eol-last": [
      "off"
    ],
    "func-call-spacing": [
      "off"
    ],
    "function-call-argument-newline": [
      "off"
    ],
    "function-paren-newline": [
      "off"
    ],
    "generator-star-spacing": [
      "off"
    ],
    "implicit-arrow-linebreak": [
      "off"
    ],
    "indent": [
      "off"
    ],
    "jsx-quotes": [
      "off"
    ],
    "key-spacing": [
      "off"
    ],
    "keyword-spacing": [
      "off"
    ],
    "linebreak-style": [
      "off"
    ],
    "lines-around-comment": [
      0
    ],
    "max-len": [
      0
    ],
    "max-statements-per-line": [
      "off"
    ],
    "multiline-ternary": [
      "off"
    ],
    "new-parens": [
      "off"
    ],
    "newline-per-chained-call": [
      "off"
    ],
    "no-confusing-arrow": [
      0
    ],
    "no-extra-parens": [
      "off"
    ],
    "no-extra-semi": [
      "off"
    ],
    "no-floating-decimal": [
      "off"
    ],
    "no-mixed-operators": [
      0
    ],
    "no-mixed-spaces-and-tabs": [
      "off"
    ],
    "no-multi-spaces": [
      "off"
    ],
    "no-multiple-empty-lines": [
      "off"
    ],
    "no-tabs": [
      0
    ],
    "no-trailing-spaces": [
      "off"
    ],
    "no-whitespace-before-property": [
      "off"
    ],
    "nonblock-statement-body-position": [
      "off"
    ],
    "object-curly-newline": [
      "off"
    ],
    "object-curly-spacing": [
      "off"
    ],
    "object-property-newline": [
      "off"
    ],
    "one-var-declaration-per-line": [
      "off"
    ],
    "operator-linebreak": [
      "off"
    ],
    "padded-blocks": [
      "off"
    ],
    "quote-props": [
      "off"
    ],
    "quotes": [
      0
    ],
    "rest-spread-spacing": [
      "off"
    ],
    "semi": [
      "off"
    ],
    "semi-spacing": [
      "off"
    ],
    "semi-style": [
      "off"
    ],
    "space-before-blocks": [
      "off"
    ],
    "space-before-function-paren": [
      "off"
    ],
    "space-in-parens": [
      "off"
    ],
    "space-infix-ops": [
      "off"
    ],
    "space-unary-ops": [
      "off"
    ],
    "switch-colon-spacing": [
      "off"
    ],
    "template-curly-spacing": [
      "off"
    ],
    "template-tag-spacing": [
      "off"
    ],
    "wrap-iife": [
      "off"
    ],
    "wrap-regex": [
      "off"
    ],
    "yield-star-spacing": [
      "off"
    ],
    "react/jsx-space-before-closing": [
      "off"
    ],
    "import/no-named-as-default": [
      1
    ],
    "import/no-named-as-default-member": [
      1
    ],
    "import/no-duplicates": [
      1
    ],
    "import/no-unresolved": [
      2
    ],
    "import/default": [
      2
    ],
    "import/export": [
      2
    ],
    "for-direction": [
      "error"
    ],
    "no-async-promise-executor": [
      "error"
    ],
    "no-case-declarations": [
      "error"
    ],
    "no-class-assign": [
      "error"
    ],
    "no-compare-neg-zero": [
      "error"
    ],
    "no-cond-assign": [
      "error"
    ],
    "no-constant-condition": [
      "error"
    ],
    "no-control-regex": [
      "error"
    ],
    "no-debugger": [
      "error"
    ],
    "no-delete-var": [
      "error"
    ],
    "no-dupe-else-if": [
      "error"
    ],
    "no-duplicate-case": [
      "error"
    ],
    "no-empty": [
      "error"
    ],
    "no-empty-character-class": [
      "error"
    ],
    "no-empty-pattern": [
      "error"
    ],
    "no-ex-assign": [
      "error"
    ],
    "no-extra-boolean-cast": [
      "error"
    ],
    "no-fallthrough": [
      "error"
    ],
    "no-global-assign": [
      "error"
    ],
    "no-inner-declarations": [
      "error"
    ],
    "no-invalid-regexp": [
      "error"
    ],
    "no-irregular-whitespace": [
      "error"
    ],
    "no-misleading-character-class": [
      "error"
    ],
    "no-nonoctal-decimal-escape": [
      "error"
    ],
    "no-octal": [
      "error"
    ],
    "no-prototype-builtins": [
      "error"
    ],
    "no-regex-spaces": [
      "error"
    ],
    "no-self-assign": [
      "error"
    ],
    "no-shadow-restricted-names": [
      "error"
    ],
    "no-sparse-arrays": [
      "error"
    ],
    "no-unsafe-finally": [
      "error"
    ],
    "no-unsafe-optional-chaining": [
      "error"
    ],
    "no-unused-labels": [
      "error"
    ],
    "no-useless-backreference": [
      "error"
    ],
    "no-useless-catch": [
      "error"
    ],
    "no-useless-escape": [
      "error"
    ],
    "no-with": [
      "error"
    ],
    "require-yield": [
      "error"
    ],
    "use-isnan": [
      "error"
    ],
    "valid-typeof": [
      "error"
    ]
  },
  "settings": {
    "import/extensions": [
      ".ts",
      ".tsx",
      ".mts",
      ".tsx",
      ".js",
      ".jsx"
    ],
    "import/parsers": {
      "@typescript-eslint/parser": [
        ".ts",
        ".tsx",
        ".mts",
        ".tsx"
      ]
    },
    "import/resolver": {
      "typescript": {
        "alwaysTryTypes": true
      },
      "node": {
        "extensions": [
          ".ts",
          ".cts",
          ".mts",
          ".tsx",
          ".js",
          ".jsx"
        ]
      }
    },
    "import/external-module-folders": [
      "node_modules",
      "node_modules/@types"
    ]
  },
  "ignorePatterns": [
    "packages/common/dist"
  ]
}

}
}

export async function main(obligation: string) {
if (!stringIsObligation(obligation)) {
throw new Error(
Expand All @@ -38,13 +56,7 @@ export async function main(obligation: string) {
message: 'Starting to process obligation resources',
});

let results: ObligationResult[];

switch (obligation) {
case 'TAGGING': {
results = await evaluateTaggingObligation(db);
}
}
const results: ObligationResult[] = await getResults(obligation, db);

console.log({
message: 'Finished processing obligation resources, saving results to DB.',
Expand Down
10 changes: 5 additions & 5 deletions packages/obligatron/src/obligations/tagging.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { PrismaClient } from '@prisma/client';
import { evaluateTaggingObligation } from './tagging';
import { evaluateSecurityHubTaggingCoverage } from './tagging';

const createPrismaClientWithMockedResponse = (response: unknown[]) => {
const aws_securityhub_findings = {
Expand Down Expand Up @@ -51,7 +51,7 @@ describe('The tagging obligation', () => {
},
]);

const results = await evaluateTaggingObligation(client);
const results = await evaluateSecurityHubTaggingCoverage(client);

expect(results).toHaveLength(2);
expect(results[0]).toEqual({
Expand Down Expand Up @@ -89,7 +89,7 @@ describe('The tagging obligation', () => {
},
]);

const results = await evaluateTaggingObligation(client);
const results = await evaluateSecurityHubTaggingCoverage(client);

expect(results).toHaveLength(0);
});
Expand Down Expand Up @@ -124,7 +124,7 @@ describe('The tagging obligation', () => {
},
]);

const results = await evaluateTaggingObligation(client);
const results = await evaluateSecurityHubTaggingCoverage(client);

expect(results).toHaveLength(2);
});
Expand All @@ -140,7 +140,7 @@ describe('The tagging obligation', () => {
},
]);

await expect(evaluateTaggingObligation(client)).rejects.toEqual(
await expect(evaluateSecurityHubTaggingCoverage(client)).rejects.toEqual(
new Error('Invalid resource in finding 123456789012'),
);
});
Expand Down
49 changes: 47 additions & 2 deletions packages/obligatron/src/obligations/tagging.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { PrismaClient } from '@prisma/client';
import type { Prisma, PrismaClient } from '@prisma/client';
import type { ObligationResult } from '.';

type FindingResource = {
Expand Down Expand Up @@ -42,7 +42,52 @@ const isFindingResource = (resource: unknown): resource is FindingResource =>
'Id' in resource &&
'Tags' in resource;

export async function evaluateTaggingObligation(
export async function evaluateAmiTaggingCoverage(
db: PrismaClient,
): Promise<ObligationResult[]> {
const records = await db.aws_ec2_images.findMany({
where: {
account_id: {
equals: db.aws_ec2_images.fields.owner_id,
},
},
});

const amiTags = [
// The "core" tags
'Stack',
'Stage',
'App',
'gu:repo',

// Tags added by AMIgo
'AmigoStage',
'SourceAMI',
'BuildNumber',
'BuiltBy',
'Recipe',
'BakeId',
];

return records.flatMap<ObligationResult>((record) => {
const tagKeys = Object.keys(record.tags as Prisma.JsonObject);

const missingTags = amiTags.filter((tag) => !tagKeys.includes(tag));
console.log(`AMI ${record.arn} is missing tags: ${missingTags.join(', ')}`);

return missingTags.map<ObligationResult>((tag) => {
return {
resource: record.arn,
reason: `AMIs should be tagged. Missing tag: ${tag}`,
contacts: {
aws_account: record.account_id,
},
};
});
});
}

export async function evaluateSecurityHubTaggingCoverage(
db: PrismaClient,
): Promise<ObligationResult[]> {
const findings = await db.aws_securityhub_findings.findMany({
Expand Down
Loading