From e2f1fcefa8fcdbb3639983415eb5006f560a64fd Mon Sep 17 00:00:00 2001 From: shashank-elastic <91139415+shashank-elastic@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:12:01 +0530 Subject: [PATCH] Add flag to update the docs/ATT&CK-coverage.md with markdown URL(s) (#4077) --- .github/workflows/attack-coverage-update.yml | 68 ++++++++++++++++++++ detection_rules/devtools.py | 44 ++++++++++--- 2 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/attack-coverage-update.yml diff --git a/.github/workflows/attack-coverage-update.yml b/.github/workflows/attack-coverage-update.yml new file mode 100644 index 000000000..daf889952 --- /dev/null +++ b/.github/workflows/attack-coverage-update.yml @@ -0,0 +1,68 @@ +name: attack-coverage-update +on: + workflow_dispatch: + inputs: + update-coverage: + description: 'Update the docs/ATT&CK-coverage.md file' + required: true + default: '--update-coverage' + +jobs: + pr: + runs-on: ubuntu-latest + + steps: + - name: Checkout detection-rules + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip cache purge + pip install .[dev] + + - name: Build release package with navigator files + run: | + python -m detection_rules dev build-release --generate-navigator + + - name: Set github config + run: | + git config --global user.email "72879786+protectionsmachine@users.noreply.github.com" + git config --global user.name "protectionsmachine" + + - name: Update navigator gist files and docs/ATT&CK-coverage.md file. + env: + GITHUB_TOKEN: "${{ secrets.NAVIGATOR_GIST_TOKEN }}" + run: | + python -m detection_rules dev update-navigator-gists "${{ github.event.inputs.update-coverage }}" + git add docs/"ATT\&CK-coverage.md" + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v7.0.3 + with: + assignees: '${{github.actor}}' + delete-branch: true + branch: "update-attack-coverage" + commit-message: "Update ATT&CK coverage URL(s) in docs/ATT&CK-coverage.md" + branch-suffix: "short-commit-hash" + base: main + title: 'Update ATT&CK coverage URL(s) in docs/ATT&CK-coverage.md' + body: | + Update ATT&CK coverage URL(s) in docs/ATT&CK-coverage.md + + - Autogenerated from job `attack-coverage-update: pr`. + labels: "backport: auto" + + - name: Archive production artifacts + uses: actions/upload-artifact@v4 + with: + name: release-files + path: | + releases diff --git a/detection_rules/devtools.py b/detection_rules/devtools.py index 96c108fae..83efbe466 100644 --- a/detection_rules/devtools.py +++ b/detection_rules/devtools.py @@ -780,7 +780,9 @@ def deprecate_rule(ctx: click.Context, rule_file: Path, deprecation_folder: Path help='GitHub token to push to gist', hide_input=True) @click.option('--gist-id', default=NAVIGATOR_GIST_ID, help='Gist ID to be updated (must exist).') @click.option('--print-markdown', is_flag=True, help='Print the generated urls') -def update_navigator_gists(directory: Path, token: str, gist_id: str, print_markdown: bool) -> list: +@click.option('--update-coverage', is_flag=True, help='Update the docs/ATT&CK-coverage.md file') +def update_navigator_gists(directory: Path, token: str, gist_id: str, print_markdown: bool, + update_coverage: bool) -> list: """Update the gists with new navigator files.""" assert directory.exists(), f'{directory} does not exist' @@ -820,17 +822,41 @@ def update_navigator_gists(directory: Path, token: str, gist_id: str, print_mark link_name = name.split('.')[0] markdown_links.append(f'|[{link_name}]({url})|') + markdown = [ + f'**Full coverage**: {NAVIGATOR_BADGE}', + '\n', + f'**Coverage by platform**: [navigator]({platforms_url})', + '\n', + '| other navigator links by rule attributes |', + '|------------------------------------------|', + ] + markdown_links + if print_markdown: - markdown = [ - f'**Full coverage**: {NAVIGATOR_BADGE}', - '\n', - f'**Coverage by platform**: [navigator]({platforms_url})', - '\n', - '| other navigator links by rule attributes |', - '|------------------------------------------|', - ] + markdown_links click.echo('\n'.join(markdown) + '\n') + if update_coverage: + coverage_file_path = get_path('docs', 'ATT&CK-coverage.md') + header_lines = textwrap.dedent(""" + # Rule coverage + + ATT&CK navigator layer files are generated when a package is built with `make release` or + `python -m detection-rules`.This also means they can be downloaded from all successful builds. + + These files can be used to pass to a custom navigator session. For convenience, the links are + generated below. You can also include multiple across tabs in a single session, though it is not + advisable to upload _all_ of them as it will likely overload your browsers resources. + + ## Current rule coverage + + The source files for these links are regenerated with every successful merge to main. These represent + coverage from the state of rules in the `main` branch. + """) + updated_file = header_lines + '\n\n' + '\n'.join(markdown) + '\n' + # Replace the old URLs with the new ones + with open(coverage_file_path, 'w') as md_file: + md_file.write(updated_file) + click.echo(f'Updated ATT&CK coverage URL(s) in {coverage_file_path}' + '\n') + click.echo(f'Gist update status on {len(generated_urls)} files: {response.status_code} {response.reason}') return generated_urls