From d285b9e84ad35b87ca5279f8d6e23f9d235677c6 Mon Sep 17 00:00:00 2001 From: Arvin <17693119+vindard@users.noreply.github.com> Date: Thu, 16 Nov 2023 15:26:05 -0400 Subject: [PATCH] build: add audit buck build steps (#3561) * build(api): add 'audit' to pnpm toolchain * build: add 'audit' step to core & apps BUCK files * build: swap new buck audit steps into github actions * build: move 'audit' check to 'test-unit' steps --- .github/workflows/audit.yml | 22 --------- apps/consent/BUCK | 9 +++- apps/dashboard/BUCK | 9 +++- core/api/BUCK | 7 +++ toolchains/workspace-pnpm/BUCK | 5 ++ toolchains/workspace-pnpm/macros.bzl | 66 +++++++++++++++++++++++++ toolchains/workspace-pnpm/run_audit.py | 55 +++++++++++++++++++++ toolchains/workspace-pnpm/toolchain.bzl | 5 ++ 8 files changed, 154 insertions(+), 24 deletions(-) delete mode 100644 .github/workflows/audit.yml create mode 100644 toolchains/workspace-pnpm/run_audit.py diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml deleted file mode 100644 index 0095b9bca1..0000000000 --- a/.github/workflows/audit.yml +++ /dev/null @@ -1,22 +0,0 @@ -#! Auto synced from Shared CI Resources repository -#! Don't change this file, instead change it in github.com/GaloyMoney/concourse-shared - -name: Audit - -on: - pull_request: - branches: [main] - -jobs: - audit: - name: Audit - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Install Nix - uses: DeterminateSystems/nix-installer-action@v4 - - name: Run the Magic Nix Cache - uses: DeterminateSystems/magic-nix-cache-action@v2 - - run: cd core/api && nix develop -c pnpm install --frozen-lockfile - - name: Run check code - run: cd core/api && nix develop -c make audit diff --git a/apps/consent/BUCK b/apps/consent/BUCK index 644ba346be..09bdcfe75b 100644 --- a/apps/consent/BUCK +++ b/apps/consent/BUCK @@ -4,7 +4,8 @@ load("@toolchains//workspace-pnpm:macros.bzl", "build_node_modules", "next_build", "next_build_bin", -"eslint" +"eslint", +"audit", ) dev_pnpm_task_binary( @@ -65,6 +66,11 @@ dev_deps_srcs = { "lib/eslint-config": "//lib/eslint-config:src", } +audit( + name = "audit", + level = "critical", +) + eslint( name = "lint", srcs = [":src"] + glob([".eslint*"]), @@ -76,6 +82,7 @@ eslint( test_suite( name = "test-unit", tests = [ + ":audit", ":lint", ], ) diff --git a/apps/dashboard/BUCK b/apps/dashboard/BUCK index 61f0d9d8c9..ae4ee902ae 100644 --- a/apps/dashboard/BUCK +++ b/apps/dashboard/BUCK @@ -4,7 +4,8 @@ load( "build_node_modules", "next_build", "next_build_bin", - "eslint" + "eslint", + "audit", ) dev_pnpm_task_binary( @@ -55,6 +56,11 @@ dev_deps_srcs = { "lib/eslint-config": "//lib/eslint-config:src", } +audit( + name = "audit", + level = "critical", +) + eslint( name = "lint", srcs = [":src"] + glob([".eslint*"]), @@ -66,6 +72,7 @@ eslint( test_suite( name = "test-unit", tests = [ + ":audit", ":lint", ], ) diff --git a/core/api/BUCK b/core/api/BUCK index be001e3423..fd204748b5 100644 --- a/core/api/BUCK +++ b/core/api/BUCK @@ -5,6 +5,7 @@ load( "tsc_build", "prod_tsc_build", "prod_tsc_build_bin", + "audit", "eslint", "typescript_check", "yaml_check", @@ -122,6 +123,11 @@ dev_update_file( out = "src/graphql/admin/schema.graphql" ) +audit( + name = "audit", + level = "critical", +) + eslint( name = "check-lint", srcs = [":src"] + [":test_src"] + glob([".eslint*"]), @@ -152,6 +158,7 @@ madge_check( test_suite( name = "test-unit", tests = [ + ":audit", ":check-lint", ":check-type", ":check-yaml", diff --git a/toolchains/workspace-pnpm/BUCK b/toolchains/workspace-pnpm/BUCK index 830135d6f4..ed62a43d88 100644 --- a/toolchains/workspace-pnpm/BUCK +++ b/toolchains/workspace-pnpm/BUCK @@ -42,3 +42,8 @@ export_file( name = "run_in_dir.py", visibility = ["PUBLIC"], ) + +export_file( + name = "run_audit.py", + visibility = ["PUBLIC"], +) diff --git a/toolchains/workspace-pnpm/macros.bzl b/toolchains/workspace-pnpm/macros.bzl index 4efee40b59..bf40792efd 100644 --- a/toolchains/workspace-pnpm/macros.bzl +++ b/toolchains/workspace-pnpm/macros.bzl @@ -608,6 +608,72 @@ def _npm_test_impl( DefaultInfo(default_output = args_file), ] +def _audit_impl(ctx: AnalysisContext) -> list[[ + DefaultInfo, + RunInfo, + ExternalRunnerTestInfo, +]]: + pnpm_toolchain = ctx.attrs._workspace_pnpm_toolchain[WorkspacePnpmToolchainInfo] + + audit_args = cmd_args() + audit_args.add("--ignore-registry-errors") + + run_cmd_args = cmd_args([ + ctx.attrs._python_toolchain[PythonToolchainInfo].interpreter, + pnpm_toolchain.run_audit[DefaultInfo].default_outputs, + "--audit-level", + ctx.attrs.level, + "--", + audit_args, + ]) + + args_file = ctx.actions.write("args.txt", run_cmd_args) + + return inject_test_run_info( + ctx, + ExternalRunnerTestInfo( + type = "audit", + command = [run_cmd_args], + ), + ) + [ + DefaultInfo(default_output = args_file), + ] + +_audit = rule( + impl = _audit_impl, + attrs = { + "level": attrs.enum( + ["low", "moderate", "high", "critical"], + default = "critical" + ), + "node_modules": attrs.source( + doc = """Target which builds `node_modules`.""", + ), + "_inject_test_env": attrs.default_only( + attrs.dep(default = "prelude//test/tools:inject_test_env"), + ), + "_python_toolchain": attrs.toolchain_dep( + default = "toolchains//:python", + providers = [PythonToolchainInfo], + ), + "_workspace_pnpm_toolchain": attrs.toolchain_dep( + default = "toolchains//:workspace_pnpm", + providers = [WorkspacePnpmToolchainInfo], + ), + }, +) + +def audit( + node_modules = ":node_modules", + visibility = ["PUBLIC"], + **kwargs): + + _audit( + node_modules = node_modules, + visibility = visibility, + **kwargs, + ) + def eslint_impl(ctx: AnalysisContext) -> list[[ DefaultInfo, RunInfo, diff --git a/toolchains/workspace-pnpm/run_audit.py b/toolchains/workspace-pnpm/run_audit.py new file mode 100644 index 0000000000..e4c801209d --- /dev/null +++ b/toolchains/workspace-pnpm/run_audit.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +""" +Runs audit for npm dependencies. +""" +import argparse +import json +import subprocess +import sys + +def sum_severities(severity_dict, start_level): + severity_order = [ + "low", + "moderate", + "high", + "critical" + ] + + start_index = severity_order.index(start_level) + return sum( + severity_dict[level] + for level in severity_order[start_index:] + ) + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--audit-level", + help="Audit severity to print advisories against.", + ) + parser.add_argument( + "args", + help="Audit arguments", + nargs=argparse.REMAINDER, + ) + + args = parser.parse_args() + audit_args = args.args[1:] # ignore '--' separator + + pnpm_cmd = ["pnpm", "audit"] + audit_cmd = [*pnpm_cmd, *audit_args] + audit_cmd_json_out = [*audit_cmd, "--json"] + + result = subprocess.run(audit_cmd_json_out, stdout=subprocess.PIPE) + result_dict = json.loads(result.stdout) + + num_vulns = sum_severities( + result_dict["metadata"]["vulnerabilities"], + args.audit_level + ) + if num_vulns > 0: + printable_result = subprocess.run(audit_cmd, stdout=subprocess.PIPE, text=True) + print(printable_result.stdout) + sys.exit(1) + + sys.exit(0) diff --git a/toolchains/workspace-pnpm/toolchain.bzl b/toolchains/workspace-pnpm/toolchain.bzl index edfc909397..141d3b4e65 100644 --- a/toolchains/workspace-pnpm/toolchain.bzl +++ b/toolchains/workspace-pnpm/toolchain.bzl @@ -8,6 +8,7 @@ WorkspacePnpmToolchainInfo = provider(fields = [ "build_next_build", "package_next_bin", "run_in_dir", + "run_audit", ]) def workspace_pnpm_toolchain_impl(ctx) -> list[[DefaultInfo, WorkspacePnpmToolchainInfo]]: @@ -26,6 +27,7 @@ def workspace_pnpm_toolchain_impl(ctx) -> list[[DefaultInfo, WorkspacePnpmToolch build_next_build = ctx.attrs._build_next_build, package_next_bin = ctx.attrs._package_next_bin, run_in_dir = ctx.attrs._run_in_dir, + run_audit = ctx.attrs._run_audit, ) ] @@ -59,6 +61,9 @@ workspace_pnpm_toolchain = rule( "_run_in_dir": attrs.dep( default = "toolchains//workspace-pnpm:run_in_dir.py", ), + "_run_audit": attrs.dep( + default = "toolchains//workspace-pnpm:run_audit.py", + ), }, is_toolchain_rule = True, )