From 030059ece8586accef1b984d858023c3584348c6 Mon Sep 17 00:00:00 2001 From: Or Ouziel Date: Thu, 14 Mar 2024 12:52:35 +0200 Subject: [PATCH] add sync integration rules workflow --- .github/workflows/sync-rule-templates.yml | 53 +++++++++++++++ scripts/common.sh | 69 +++++++++++++++++++ scripts/sync_rule_templates.sh | 80 +++++++++++++++++++++++ 3 files changed, 202 insertions(+) create mode 100644 .github/workflows/sync-rule-templates.yml create mode 100755 scripts/sync_rule_templates.sh diff --git a/.github/workflows/sync-rule-templates.yml b/.github/workflows/sync-rule-templates.yml new file mode 100644 index 0000000000..8fd9a23483 --- /dev/null +++ b/.github/workflows/sync-rule-templates.yml @@ -0,0 +1,53 @@ +name: Sync CIS Rule Templates + +on: + push: + branches: + - main + paths: + - "security-policies/bundle/compliance/**/rules/**/data.yaml" + +env: + GITHUB_TOKEN: ${{ secrets.CLOUDSEC_MACHINE_TOKEN }} + +jobs: + Sync-Templates: + name: Sync CIS Rule Templates + runs-on: ubuntu-22.04 + timeout-minutes: 60 + steps: + - name: Checkout Integrations repo + uses: actions/checkout@v4 + with: + token: ${{ secrets.CLOUDSEC_MACHINE_TOKEN }} + repository: elastic/integrations + path: integrations + + - name: Checkout Cloudbeat repo + uses: actions/checkout@v4 + with: + token: ${{ secrets.CLOUDSEC_MACHINE_TOKEN }} + path: cloudbeat + + - name: Init Hermit + working-directory: cloudbeat + run: ./bin/hermit env -r >> $GITHUB_ENV + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.9" + + - name: Install Poetry + working-directory: cloudbeat + run: | + curl -sSL https://install.python-poetry.org | python3 - + poetry --version + + - name: Install dependencies + working-directory: cloudbeat/security-policies + run: poetry install + + - name: Sync CIS Rules with integrations repo + working-directory: cloudbeat + run: scripts/sync_rule_templates.sh diff --git a/scripts/common.sh b/scripts/common.sh index a841f15765..7255b6fab2 100644 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -61,3 +61,72 @@ restart_agents() { exec_pod $POD "elastic-agent restart" done } + +bump_preview_version() { + local current_version="${1:?Missing current version}" + preview_number="${current_version##*-preview}" + ((next_preview_number = 10#${preview_number} + 1)) + echo "${current_version%-*}-preview$(printf "%02d" $next_preview_number)" +} + +bump_minor_version() { + local version="${1:?Missing version}" + IFS='.' read -r major minor _ <<<"$1" + ((minor++)) + echo "$major.$minor.0" +} + +get_integration_version() { + local changelog_path="${1:?Missing changelog.yml path}" + current_version=$(yq '.[0].version' "$changelog_path" | tr -d '"') + echo "$current_version" +} + +get_new_integration_version_map_entry() { + local latest_version="${1:?Missing latest versions entry}" + IFS='-' read -r group1 group2 <<<"$latest_version" + IFS='.' read -r major1 minor1 _ <<<"$group1" + IFS='.' read -r major2 minor2 _ <<<"$group2" + ((minor1++)) + ((minor2++)) + local first_version="${major1}.${minor1}.x" + local second_version="$(echo "${major2}.${minor2}.x" | xargs)" + echo "$first_version - $second_version" +} + +# bumps existing preview version: 1.0.0-preview01 -> 1.0.0-preview02, or +# creates a new preview version: 1.0.0 -> 1.1.0-preview01, and +# updates the manifest and changelog files +bump_integration_version() { + changelog_path="${1:?Missing changelog.yml path}" + manifest_path="${2:?Missing manifest.yml path}" + pr_url="${3:?Missing PR URL}" + changelog_description="${4:-Bump version}" + # exports required for yq's env() + export changelog_description + export pr_url + version="$(get_integration_version "$changelog_path")" + if [[ $version == *"preview"* ]]; then + next_version=$(bump_preview_version "$version") + export next_version + # update current version and add new changes entry + yq -i ".[0].version = \"$next_version\"" "$changelog_path" + yq -i '.[0].changes += [{"description": env(changelog_description), "type": "enhancement", "link": env(pr_url) }]' "$changelog_path" + else + next_version="$(bump_minor_version "$version")-preview01" + export next_version + # add new version + changes entry + yq -i '. = [{"version": env(next_version), "changes": [{"description": env(changelog_description), "type": "enhancement", "link": env(pr_url) }]}] + .' "$changelog_path" + + # add new version map for integration - kibana + latest_entry="$(sed -n '3p' "$changelog_path")" + next_entry=$(get_new_integration_version_map_entry "$latest_entry") + sed -i '' -e '3i\'$'\n'"$next_entry" "$changelog_path" + + # update manifest with new kibana version + IFS='-' read -r _ next_kibana_version <<<"$next_entry" + IFS='.' read -r major minor _ _ <<<"$(echo "$next_kibana_version" | xargs)" + yq -i ".conditions.kibana.version = \"^$major.$minor.0\"" "$manifest_path" + fi + yq -i ".version = \"$next_version\"" "$manifest_path" +} diff --git a/scripts/sync_rule_templates.sh b/scripts/sync_rule_templates.sh new file mode 100755 index 0000000000..2b9fa67302 --- /dev/null +++ b/scripts/sync_rule_templates.sh @@ -0,0 +1,80 @@ +#!/bin/bash +set -euo pipefail + +source scripts/common.sh + +git config --global user.email "cloudsecmachine@users.noreply.github.com" +git config --global user.name "Cloud Security Machine" + +branch_name="sync-cis-rule-templates" +repo="repos/elastic/integrations" +pr_number=$(gh api $repo/pulls -q ".[] | select(.head.ref == \"$branch_name\" and .state == \"open\") | .number") +templates_path="packages/cloud_security_posture/kibana/csp_rule_template" +manifest_path="packages/cloud_security_posture/manifest.yml" +changelog_path="packages/cloud_security_posture/changelog.yml" + +# get new or existing sync-cis-rule-templates branch +cd ../integrations +if git fetch origin main "$branch_name" &>/dev/null; then + git checkout "$branch_name" + git reset origin/main --hard # reset to main, avoids conflicts +else + git checkout -b "$branch_name" origin/main +fi + +# generate the rule templates +cd ../cloudbeat +poetry run -C security-policies python security-policies/dev/generate_rule_templates.py + +# commit and push the changes +cd ../integrations +git add "$templates_path" +git commit -m "Sync CIS rule templates" +git push origin "$branch_name" -f + +# create a PR if it doesn't exist and assign labels +if [[ -z "$pr_number" ]]; then + pr=$(gh api \ + --method POST \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + /$repo/pulls \ + -f title='[Cloud Security] Sync CIS rule templates' \ + -f body='' \ + -f head="$branch_name" \ + -f base='main') + + pr_number=$(echo "$pr" | jq -r '.html_url' | awk -F'/' '{print $NF}') + + gh api \ + --method POST \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "/$repo/issues/$pr_number/labels" \ + -f "labels[]=Team:Cloud Security" -f "labels[]=enhancement" +fi + +pr_url=$(gh api $repo/pulls -q ".[] | select(.head.ref == \"$branch_name\" and .state == \"open\") | .html_url") +bump_integration_version "$changelog_path" "$manifest_path" "$pr_url" "Add CIS rule templates" +git add "$changelog_path" "$manifest_path" +git commit -m "Bump integration version" +git push origin "$branch_name" + +# create PR body +rows="$(git diff --name-only origin/main -- "$templates_path" | while read -r file; do jq --arg a "$pr_url/files#diff-$(echo -n "$file" | openssl dgst -sha256 | awk '{print $2}')" -r '.attributes.metadata.benchmark | "\(.id): \(.rule_number): \($a)"' "$file"; done | awk '{split($0, a, ": "); b[a[1]] = (b[a[1]] == "" ? "" : b[a[1]] ", ") "["a[2]"]""("a[3]")"} END {for (i in b) printf("| %s | %s |\n", i, b[i])}')" +body=$( + cat <