diff --git a/.github/FALCO_VERSIONS b/.github/FALCO_VERSIONS new file mode 100644 index 000000000..1f7391f92 --- /dev/null +++ b/.github/FALCO_VERSIONS @@ -0,0 +1 @@ +master diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 4d38a3880..4ad907516 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,15 +1,17 @@ **What type of PR is this?** > Uncomment one (or more) `/kind <>` lines: +> /kind feature + > /kind bug > /kind cleanup @@ -20,8 +22,6 @@ > /kind failing-test -> /kind feature - +**Proposed rule [maturity level](https://github.com/falcosecurity/rules/blob/main/CONTRIBUTING.md#maturity-levels)** + +> Uncomment one (or more) `/area <>` lines (only for PRs that add or modify rules): + +> /area maturity-stable + +> /area maturity-incubating + +> /area maturity-sandbox + +> /area maturity-deprecated + + + **What this PR does / why we need it**: **Which issue(s) this PR fixes**: @@ -50,7 +66,7 @@ Please remove the leading whitespace before the `/area <>` you uncommented. Fixes # diff --git a/.github/compare-rule-files.sh b/.github/compare-rule-files.sh new file mode 100755 index 000000000..07f4a1c69 --- /dev/null +++ b/.github/compare-rule-files.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +RULES_FILE=$1 +RESULT_FILE=$2 +CHECKER_TOOL=$3 +FALCO_DOCKER_IMAGE=$4 + +set -e pipefail + +rm -f $RESULT_FILE +touch $RESULT_FILE + +cur_branch=`git rev-parse HEAD` +echo Current branch is \"$cur_branch\" +echo Checking version for rules file \"$RULES_FILE\"... +cp $RULES_FILE tmp_rule_file.yaml + +rules_name=`echo $RULES_FILE | sed -re 's/rules\/(.*)_rules\.yaml/\1/'` +echo Searching tag with prefix prefix \"$rules_name-rules-\"... +git_rev=`git rev-list --tags="$rules_name-rules-*.*.*" --max-count=1` + +if [ -z "$git_rev" ] +then + echo Not previous tag has been found + exit 0 +fi + +latest_tag=`git describe --match="$rules_name-rules-*.*.*" --exclude="$rules_name-rules-*.*.*-*" --abbrev=0 --tags ${git_rev} || echo ""` + +if [ -z "$latest_tag" ] +then + echo Not previous tag has been found + exit 0 +else + echo Most recent tag found is \"$latest_tag\" +fi + +git checkout tags/$latest_tag +chmod +x $CHECKER_TOOL +$CHECKER_TOOL \ + compare \ + --falco-image=$FALCO_DOCKER_IMAGE \ + -l $RULES_FILE \ + -r tmp_rule_file.yaml \ +1>tmp_res.txt +git switch --detach $cur_branch + +echo '##' $(basename $RULES_FILE) >> $RESULT_FILE +echo Comparing \`$cur_branch\` with latest tag \`$latest_tag\` >> $RESULT_FILE +echo "" >> $RESULT_FILE +if [ -s tmp_res.txt ] +then + cat tmp_res.txt >> $RESULT_FILE +else + echo "No changes detected" >> $RESULT_FILE +fi +echo "" >> $RESULT_FILE + +if $(git branch --show-current | grep -q "release/"); then + if $(cat $RESULT_FILE | grep -q "\*\*Minor\*\* changes") || $(cat $RESULT_FILE | grep -q "\*\*Major\*\* changes"); then + echo "**Notes**:" >> $RESULT_FILE + echo "" >> $RESULT_FILE + echo "This PR proposes merging major or minor changes into a release branch. Please make sure this is intentional. cc @falcosecurity/rules-maintainers" >> $RESULT_FILE + echo "" >> $RESULT_FILE + echo "/hold" >> $RESULT_FILE + echo "" >> $RESULT_FILE + fi +fi + +rm -f tmp_rule_file.yaml +rm -f tmp_res.txt diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..722812a57 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + groups: + actions: + update-types: + - "minor" + - "patch" diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt new file mode 100644 index 000000000..bb9033cfd --- /dev/null +++ b/.github/scripts/requirements.txt @@ -0,0 +1,3 @@ +pandas==2.0.3 +pyyaml===6.0.1 +tabulate==0.9.0 \ No newline at end of file diff --git a/.github/scripts/rules_overview_generator.py b/.github/scripts/rules_overview_generator.py new file mode 100644 index 000000000..9b0b6488e --- /dev/null +++ b/.github/scripts/rules_overview_generator.py @@ -0,0 +1,160 @@ +import pandas as pd +import yaml +import argparse +import datetime +import os +import sys +import re + +""" +Usage: +pip install -r .github/scripts/requirements.txt +python .github/scripts/rules_overview_generator.py --rules_dir=rules > docs/index.md +""" + +BASE_MITRE_URL_TECHNIQUE="https://attack.mitre.org/techniques/" +BASE_MITRE_URL_TACTIC="https://attack.mitre.org/tactics/" +BASE_PCI_DSS="https://docs-prv.pcisecuritystandards.org/PCI%20DSS/Standard/PCI-DSS-v4_0.pdf" +BASE_NIST="https://csf.tools/reference/nist-sp-800-53/r5/" +COLUMNS=['maturity', 'rule', 'desc', 'workload', 'mitre_phase', 'mitre_ttp', 'extra_tags', 'compliance_pci_dss', 'compliance_nist', 'extra_tags_list', 'mitre_phase_list', 'compliance_pci_dss_list', 'compliance_nist_list', 'enabled'] + +def arg_parser(): + parser = argparse.ArgumentParser() + parser.add_argument('--rules_dir', help='Path to falco rules directory containing all rules yaml files.') + return parser.parse_args() + +def rules_to_df(rules_dir): + l = [] + for rules_filename in os.listdir(rules_dir): + if not 'falco' in rules_filename: + continue + with open(os.path.join(rules_dir, rules_filename), 'r') as f: + items = yaml.safe_load(f) + for item in items: + if 'rule' in item and 'tags' in item: + if len(item['tags']) > 0: + item['maturity'], item['workload'], item['mitre_phase'], item['mitre_ttp'], item['compliance_pci_dss'], item['compliance_nist'], item['extra_tags'] = [], [], [], [], [], [], [] + for i in item['tags']: + if i.startswith('maturity_'): + item['maturity'].append(i) # should be just one per rule, be resilient and treat as list as well + elif i.startswith('PCI_DSS_'): + item['compliance_pci_dss'].append('[{}]({})'.format(i, BASE_PCI_DSS)) + elif i.startswith('NIST_800-53_'): + # NIST links: revisit in the future, could be fragile + item['compliance_nist'].append('[{}]({}{}/{})'.format(i, BASE_NIST, re.search('NIST_800-53_(.*)-', i, re.IGNORECASE).group(1).lower(), \ + i.replace('NIST_800-53_', '').lower())) + elif i in ['host', 'container']: + item['workload'].append(i) + elif i.startswith('mitre_'): + item['mitre_phase'].append(i) + elif i.startswith('T'): + if i.startswith('TA'): + item['mitre_ttp'].append('[{}]({}{})'.format(i, BASE_MITRE_URL_TACTIC, i.replace('.', '/'))) + else: + item['mitre_ttp'].append('[{}]({}{})'.format(i, BASE_MITRE_URL_TECHNIQUE, i.replace('.', '/'))) + else: + item['extra_tags'].append(i) + item['workload'].sort() + item['mitre_phase'].sort() + item['mitre_ttp'].sort() + item['compliance_pci_dss'].sort() + item['compliance_nist'].sort() + item['mitre_phase_list'] = item['mitre_phase'] + item['extra_tags_list'] = item['extra_tags'] + item['compliance_pci_dss_list'] = item['compliance_pci_dss'] + item['compliance_nist_list'] = item['compliance_nist'] + item['enabled'] = (item['enabled'] if 'enabled' in item else True) + l.append([', '.join(item[x]) if x in ['maturity', 'workload', 'mitre_phase', 'mitre_ttp', 'compliance_pci_dss', 'compliance_nist', 'extra_tags'] else item[x] for x in COLUMNS]) + + if not l: + sys.exit('No valid rules in any of the falco_rules.* files in the rules_dir or no falco_rules.* files in the rules_dir in the first place, exiting ...') + df = pd.DataFrame.from_records(l, columns=COLUMNS) + return df.sort_values(by=['maturity','rule'], inplace=False) + +def print_markdown(df): + n_rules=len(df) + df_overview = df.drop(['extra_tags_list', 'mitre_phase_list', 'compliance_pci_dss_list', 'compliance_nist_list'], axis=1) + maturity_col_name = '
maturity
' + df_overview.rename(columns={ \ + 'maturity': maturity_col_name, \ + 'rule': '
rule
', \ + 'desc': '
desc
', \ + 'workload': '
workload
', \ + 'mitre_phase': '
mitre_phase
', \ + 'mitre_ttp': '
mitre_ttp
', \ + 'extra_tags': '
extra_tags
', \ + 'compliance_pci_dss': '
compliance_pci_dss
', \ + 'compliance_nist': '
compliance_nist
', \ + 'enabled': '
enabled
', \ + }, inplace=True) + + df_stable = df_overview[(df_overview[maturity_col_name] == 'maturity_stable')] + df_incubating = df_overview[(df_overview[maturity_col_name] == 'maturity_incubating')] + df_sandbox = df_overview[(df_overview[maturity_col_name] == 'maturity_sandbox')] + df_deprecated = df_overview[(df_overview[maturity_col_name] == 'maturity_deprecated')] + + print('# Falco Rules Overview\n') + print('Last Updated: {}\n'.format(datetime.date.today())) + print('This auto-generated document is derived from the `falco*_rules.yaml` files within the [rules](https://github.com/falcosecurity/rules/blob/main/rules/) directory of the main branch in the official Falco [rules repository](https://github.com/falcosecurity/rules/tree/main).\n') + print('The Falco Project manages a total of {} [rules](https://github.com/falcosecurity/rules/blob/main/rules/), of which {} rules are included in the Falco release package and labeled with [maturity_stable](https://github.com/falcosecurity/rules/blob/main/CONTRIBUTING.md#rules-maturity-framework). Rules at the remaining maturity levels require explicit installation and may need extra customization to ensure effective adoption. Lastly, certain rules are intentionally disabled by default, irrespective of their maturity level.\n'.format(n_rules, len(df_stable))) + print('This document provides an extensive overview of community-contributed syscall and container event-based rules. It offers resources for learning about these rules, promoting successful adoption, and driving future enhancements.\n') + print('\n[Stable Falco Rules](#stable-falco-rules) | [Incubating Falco Rules](#incubating-falco-rules) | [Sandbox Falco Rules](#sandbox-falco-rules) | [Deprecated Falco Rules](#deprecated-falco-rules) | [Falco Rules Stats](#falco-rules-stats)\n') + print('\nThe tables below can be scrolled to the right.\n') + + print('\n## Stable Falco Rules\n') + print('\n{} stable Falco rules ({:.2f}% of rules) are included in the Falco release package:\n'.format(len(df_stable), (100.0 * len(df_stable) / n_rules))) + print(df_stable.to_markdown(index=False)) + + print('\n## Incubating Falco Rules\n') + print('\n{} incubating Falco rules ({:.2f}% of rules):\n'.format(len(df_incubating), (100.0 * len(df_incubating) / n_rules))) + print(df_incubating.to_markdown(index=False)) + + print('\n## Sandbox Falco Rules\n') + print('\n{} sandbox Falco rules ({:.2f}% of rules):\n'.format(len(df_sandbox), (100.0 * len(df_sandbox) / n_rules))) + print(df_sandbox.to_markdown(index=False)) + + print('\n## Deprecated Falco Rules\n') + print('\n{} deprecated Falco rules ({:.2f}% of rules):\n'.format(len(df_deprecated), (100.0 * len(df_deprecated) / n_rules))) + print(df_deprecated.to_markdown(index=False)) + + print('\n# Falco Rules Stats\n') + print('\n### Falco rules per workload type:\n') + df1 = df.groupby('workload').agg(rule_count=('workload', 'count')) + df1['percentage'] = round(100.0 * df1['rule_count'] / df1['rule_count'].sum(), 2).astype(str) + '%' + print(df1.to_markdown(index=True)) + + print('\n### Falco rules per [Mitre Attack](https://attack.mitre.org/) phase:\n') + df2 = df[['rule', 'maturity', 'mitre_phase_list']].explode('mitre_phase_list') + df2.rename(columns={'mitre_phase_list':'mitre_phase'}, inplace=True) + df2.sort_values(by=['mitre_phase','rule'], inplace=True) + df2['rule'] = df[['maturity', 'rule']].agg(': '.join, axis=1) + mitre_phase_col_name = '
mitre_phase
' + df2.rename(columns={'mitre_phase': mitre_phase_col_name, \ + }, inplace=True) + df2 = df2.groupby(mitre_phase_col_name).agg({'rule': lambda x: ['\n'.join(list(x)), len(list(x))]}) + df2['
rules
'] = df2['rule'].apply(lambda x: x[0]) + df2['
percentage
'] = df2['rule'].apply(lambda x: round((100.0 * x[1] / n_rules), 2)).astype(str) + '%' + print(df2.drop('rule', axis=1).to_markdown(index=True)) + + print('\n### Compliance-related Falco rules:\n') + df3 = df + df3['compliance_tag'] = df['compliance_pci_dss_list'] + df['compliance_nist_list'] + df3.sort_values(by=['rule'], inplace=True) + compliance_tag_col_name = '
compliance_tag
' + df3.rename(columns={'compliance_tag': compliance_tag_col_name, \ + }, inplace=True) + df3 = df3[['rule', compliance_tag_col_name, 'maturity']].explode(compliance_tag_col_name) + df3 = df3.groupby(compliance_tag_col_name).agg({'rule': lambda x: ['\n'.join(list(x)), len(list(x))]}) + df3['
rules
'] = df3['rule'].apply(lambda x: x[0]) + # df3['percentage'] = df3['rule'].apply(lambda x: round((100.0 * x[1] / n_rules), 2)).astype(str) + '%' + print(df3.drop('rule', axis=1).to_markdown(index=True)) + + +if __name__ == '__main__': + args_parsed = arg_parser() + rules_dir = args_parsed.rules_dir + + if not rules_dir or not os.path.isdir(rules_dir): + sys.exit('No valid rules directory provided via --rules_dir arg, exiting ...') + + print_markdown(rules_to_df(rules_dir)) diff --git a/.github/workflows/create-comment.yaml b/.github/workflows/create-comment.yaml new file mode 100644 index 000000000..5cf569614 --- /dev/null +++ b/.github/workflows/create-comment.yaml @@ -0,0 +1,54 @@ +# NOTE: This has read-write repo token and access to secrets, so this must +# not run any untrusted code. +# see: https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ +name: Comment on the pull request + +on: + workflow_run: + workflows: ["Rules"] + types: + - completed + +jobs: + upload: + runs-on: ubuntu-latest + if: github.event.workflow_run.event == 'pull_request' + steps: + - name: 'Download artifact' + uses: actions/github-script@v7.0.1 + with: + script: | + var artifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: ${{github.event.workflow_run.id }}, + }); + var matchArtifact = artifacts.data.artifacts.filter((artifact) => { + return artifact.name == "pr" + })[0]; + var download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + }); + var fs = require('fs'); + fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); + + - name: 'Unpack artifact' + run: unzip pr.zip + + - name: 'Comment on PR' + uses: actions/github-script@v7.0.1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + var fs = require('fs'); + var issue_number = Number(fs.readFileSync('./NR')); + var comment_body = fs.readFileSync('./COMMENT'); + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issue_number, + body: comment_body.toString('utf8') + }); diff --git a/.github/workflows/pages.yaml b/.github/workflows/pages.yaml new file mode 100644 index 000000000..6c510768f --- /dev/null +++ b/.github/workflows/pages.yaml @@ -0,0 +1,46 @@ +name: Deploy Github Pages +on: + push: + branches: [main] + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: true + +jobs: + deploy-pages: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: 3.x + + - name: Generate updated inventory + run: | + pip install -r .github/scripts/requirements.txt + python .github/scripts/rules_overview_generator.py --rules_dir=rules > docs/index.md + + - name: Disable Table Of Content for overview + run: | + sed -i '1s/^/---\nhide:\n- toc\n---\n\n/' docs/index.md + + - run: pip install mkdocs mkdocs-material + + - run: mkdocs build + + - uses: actions/upload-pages-artifact@v3 + with: + path: 'site' + + - id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/ci.yaml b/.github/workflows/registry.yaml similarity index 61% rename from .github/workflows/ci.yaml rename to .github/workflows/registry.yaml index c3246c7b5..1065d4974 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/registry.yaml @@ -1,25 +1,30 @@ -name: CI +name: Registry on: pull_request: - branches: [main] + branches: + - main jobs: - check-registry: + validate: runs-on: ubuntu-latest steps: - name: Checkout Rules - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Golang - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: go-version: '^1.19' - name: Build registry artifact tool working-directory: build/registry run: go build -o rules-registry ./... + + - name: Test registry artifact tool + working-directory: build/registry + run: go test ./... -cover - name: Check Registry validity run: build/registry/rules-registry check registry.yaml diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 2a579298e..efeddd2ff 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -9,25 +9,31 @@ on: jobs: release-rulesfile: runs-on: ubuntu-latest + env: + OCI_REGISTRY: ghcr.io + AWS_S3_BUCKET: falco-distribution AWS_S3_PREFIX: rules AWS_S3_REGION: eu-west-1 - # These permissions are needed to interact with GitHub's OIDC Token endpoint. permissions: + # These permissions are needed to interact with GitHub's OIDC Token endpoint. id-token: write contents: read packages: write steps: + + # Get rules repository - name: Checkout Rules - uses: actions/checkout@v3 + uses: actions/checkout@v4 + # Get registry artifact tool - name: Setup Golang - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: '^1.19' + go-version-file: build/registry/go.mod - name: Build registry artifact tool working-directory: build/registry @@ -38,21 +44,34 @@ jobs: echo "OCI_REPO_PREFIX=ghcr.io/${GITHUB_REPOSITORY,,}" >>${GITHUB_ENV} - name: Upload OCI artifacts to GitHub packages + id: oci_build env: REGISTRY_USER: ${{ github.repository_owner }} REGISTRY_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_REPO_URL: ${{ github.server_url }}/${{ github.repository }}.git # uses OCI_REPO_PREFIX environment variable - run: build/registry/rules-registry push-to-oci registry.yaml ${{ github.ref_name }} + run: >- + echo "ARTIFACT_REPO_DIGEST=$( + build/registry/rules-registry push-to-oci registry.yaml ${{ github.ref_name }} + )" >> $GITHUB_OUTPUT + + # Create a signature of the rules artifact as OCI artifact + - name: Install Cosign + uses: sigstore/cosign-installer@v3.5.0 + + - name: Login with cosign + run: cosign login $OCI_REGISTRY --username ${{ github.repository_owner }} --password ${{ secrets.GITHUB_TOKEN }} + + - name: Sign the images with GitHub OIDC Token + run: cosign sign --yes ${{ steps.oci_build.outputs.ARTIFACT_REPO_DIGEST }} - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v1 + uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: "arn:aws:iam::292999226676:role/terraform-20230120142903096000000002" aws-region: ${{ env.AWS_S3_REGION }} - name: Upload files to S3 - # uses AWS_S3_BUCKET, AWS_S3_PREFIX, AWS_S3_REGION environment variables run: build/registry/rules-registry upload-to-s3 registry.yaml ${{ github.ref_name }} diff --git a/.github/workflows/rules.yaml b/.github/workflows/rules.yaml new file mode 100644 index 000000000..6cbd5f183 --- /dev/null +++ b/.github/workflows/rules.yaml @@ -0,0 +1,195 @@ +name: Rules + +on: + pull_request: + branches: + - main + - release/* + push: + branches: + - main + +jobs: + # retrieves the changed rules files and the Falco versions to be used + get-values: + runs-on: ubuntu-latest + outputs: + changed-files: ${{ steps.set-changed-files.outputs.changed-files }} + falco-versions: ${{ steps.set-falco-versions.outputs.versions }} + steps: + - name: Checkout rules + uses: actions/checkout@v4 + + - name: Get changed files + id: changed-files + if: github.event_name == 'pull_request' + uses: Ana06/get-changed-files@v2.3.0 + with: + format: space-delimited + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Find changed rules files + id: set-changed-files + run: | + # Find any changed file located under the /rules folder that matches the naming convention _rules.yaml. + # See https://github.com/falcosecurity/rules/blob/main/README.md#naming-convention for details. + # Additionally, if we skip changed-files because we're not in a pull request, + # then we consider all the rules contained in the repository. + all_files="${{ steps.changed-files.outputs.all }}" + values="" + if [ -z "$all_files" ]; then + values=$(ls rules/*_rules.yaml) + else + for changed_file in $all_files; do + if [[ "${changed_file}" =~ ^rules/[^/]*_rules\.yaml$ ]]; then + values=${values}${changed_file}$'\n' + fi + done + fi + echo "changed-files=$(echo "${values}" | jq -R -s -c 'split("\n")' | jq -c 'map(select(length > 0))')" >> $GITHUB_OUTPUT + + - name: Read Falco versions + id: set-falco-versions + run: | + values="" + while read -r line + do + values="${values}${line}"$'\n' + done < "./.github/FALCO_VERSIONS" + echo "versions=$(echo "${values}" | jq -R -s -c 'split("\n")' | jq -c 'map(select(length > 0))')" >> $GITHUB_OUTPUT + + validate: + if: needs.get-values.outputs.changed-files != '[]' && needs.get-values.outputs.changed-files != '' + needs: get-values + strategy: + fail-fast: false + matrix: + rules-file: ${{ fromJson(needs.get-values.outputs.changed-files) }} + falco-version: ${{ fromJson(needs.get-values.outputs.falco-versions) }} + runs-on: ubuntu-latest + steps: + - name: Setup Golang + uses: actions/setup-go@v5 + with: + go-version: "1.19.0" + + - name: Checkout rules + uses: actions/checkout@v4 + + - name: Build checker tool + working-directory: build/checker + run: go build -o rules-check + + - name: Test checker tool + working-directory: build/checker + run: go test ./... -cover + + - name: Validate rules file + run: | + build/checker/rules-check \ + validate \ + --falco-image="falcosecurity/falco-no-driver:${{ matrix.falco-version }}" \ + -r ${{ matrix.rules-file }} + + check-version: + if: github.event_name == 'pull_request' && needs.get-values.outputs.changed-files != '[]' && needs.get-values.outputs.changed-files != '' + needs: get-values + env: + # note(jasondellaluce): using the most recent targeted Falco version + FALCO_VERSION: ${{ fromJson(needs.get-values.outputs.falco-versions)[0] }} + strategy: + fail-fast: false + matrix: + rules-file: ${{ fromJson(needs.get-values.outputs.changed-files) }} + runs-on: ubuntu-latest + steps: + - name: Setup Golang + uses: actions/setup-go@v5 + with: + go-version: "1.19.0" + + - name: Checkout rules + uses: actions/checkout@v4 + + - name: Get all git tags + run: git fetch --tags origin + + - name: Get changed files + uses: Ana06/get-changed-files@v2.3.0 + id: changed + with: + format: space-delimited + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Build checker tool + working-directory: build/checker + run: go build -o rules-check + + - name: Test checker tool + working-directory: build/checker + run: go test ./... -cover + + - name: Compare changed files with previous versions + id: compare + run: | + ./.github/compare-rule-files.sh \ + "${{ matrix.rules-file }}" \ + result.txt \ + build/checker/rules-check \ + "falcosecurity/falco-no-driver:$FALCO_VERSION" + if [ -s result.txt ]; then + echo "comment_file=result.txt" >> $GITHUB_OUTPUT + fi + + - name: Save PR info + if: steps.compare.outputs.comment_file != '' + run: | + mkdir -p ./pr + cp ${{ steps.compare.outputs.comment_file }} ./pr/COMMENT-${{ strategy.job-index }} + + - name: Upload PR info as artifact + uses: actions/upload-artifact@v4 + if: steps.compare.outputs.comment_file != '' + with: + name: pr-${{ strategy.job-index }} + path: pr/ + retention-days: 1 + + upload-pr-info: + needs: [get-values, check-version] + if: ${{ !cancelled() && github.event_name == 'pull_request' && needs.get-values.outputs.changed-files != '[]' && needs.get-values.outputs.changed-files != '' }} + runs-on: ubuntu-latest + steps: + - name: Download PR infos + uses: actions/download-artifact@v4 + with: + path: tmp-artifacts + + - name: Save PR info + run: | + if [ ! -d "./tmp-artifacts/" ]; then + echo "No PR info found. Skipping." + exit 0 + fi + mkdir -p ./pr + echo ${{ github.event.number }} > ./pr/NR + touch ./pr/COMMENT + echo "# Rules files suggestions" >> ./pr/COMMENT + echo "" >> ./pr/COMMENT + files=$(find ./tmp-artifacts/) + for file in $files; do + if [[ "$file" =~ "COMMENT" ]]; then + cat "$file" >> ./pr/COMMENT + fi + done + echo Uploading PR info... + cat ./pr/COMMENT + echo "" + + - name: Upload PR info as artifact + uses: actions/upload-artifact@v4 + with: + name: pr + path: pr/ + retention-days: 1 + if-no-files-found: warn diff --git a/.github/workflows/yaml-lint.yaml b/.github/workflows/yaml-lint.yaml new file mode 100644 index 000000000..202e1c638 --- /dev/null +++ b/.github/workflows/yaml-lint.yaml @@ -0,0 +1,17 @@ +name: Yamllint Github Actions +on: + pull_request: + branches: + - main + +jobs: + lintFalcoRules: + name: Yamllint + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: yaml-lint + uses: ibiqlik/action-yamllint@v3 + with: + file_or_dir: rules/*.yaml diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..8e79fb278 --- /dev/null +++ b/.gitignore @@ -0,0 +1,196 @@ +# Specific project files +build/mitre_attack_checker/build +build/mitre_attack_checker/reports +**/falco_rules_mitre_errors.json +**/application_rules_errors.json + +# IntelliJ project files +.idea +*.iml +out +gen +### Go template +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + +### Python template +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ +.run/ + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..8b4d7b67d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,109 @@ +# Contributing + +Thank you for your interest in contributing to Falco's rules! + +This repository includes a dedicated guide for contributing rules, outlining the definitions of the rules maturity framework and the criteria for rule acceptance. This guide inherits from the general [contributing](https://github.com/falcosecurity/.github/blob/main/CONTRIBUTING.md) guide. + +All rules must be licensed under the [Apache 2.0 License](./LICENSE). + + +**Table of Contents** + +* [Rules Maturity Framework](#rules-maturity-framework) +* [Rules Acceptance Criteria](#rules-acceptance-criteria) + + +# Rules Maturity Framework + +The rules maturity framework was established following this [proposal](proposals/20230605-rules-adoption-management-maturity-framework.md). + +At a high level, The Falco Project maintains community-contributed syscall and container event-based [rules](https://github.com/falcosecurity/rules/blob/main/rules/), with `maturity_stable` tagged rules being included in the Falco release package. Other maturity-level rules are released separately, requiring explicit installation and possible customization for effective. In essence, there are now dedicated rule files for each maturity level. + +The next sections will dive deeper into how the framework works and offer guidance on selecting a maturity level for specific rules. + +## Overall Guidelines + +As specified in the tags section of the [Style Guide of Falco Rules](https://falco.org/docs/rules/style-guide/#tags), every rule upstreamed to The Falco Project must include a maturity level as its first tag. + +A new rule typically starts as `maturity_sandbox` and, in some cases, as `maturity_incubating`. However, it cannot immediately be at the `maturity_stable` level. + +Only rules at the `maturity_stable` level are distributed with the Falco release package and live in the established `falco_rules.yaml` file. All rules at the remaining maturity levels can be found in the Falco rules file according to their respective levels, and they need to be installed separately. They are made available to the adopter through the same means as the `falco_rules.yaml` file, either by directly retrieving them from this repository or by fetching them via `falcoctl`. Adopters have the flexibility to choose how they install and customize the upstream rules to suit their needs. + +Rules files: + +``` +falco_rules.yaml +falco-incubating_rules.yaml +falco-sandbox_rules.yaml +falco-deprecated_rules.yaml +``` + +Falco offers configurability through the [falco.yaml](https://github.com/falcosecurity/falco/blob/master/falco.yaml) file, enabling support for the unique use cases of adopters. This configurability allows adopters to determine which rules should be loaded based on tags and other properties of the rules. With Falco 0.36 and beyond, it's now possible to apply multiple rules that match the same event, eliminating concerns about rule prioritization based on the "first match wins" principle. + +Special note regarding *plugins* rules: The rules for different Falco [plugins](https://github.com/falcosecurity/plugins) are currently not integrated into this rules maturity framework. + + +## Maturity Levels + +The levels: + +- **maturity_stable** indicates that the rule has undergone thorough evaluation by experts with hands-on production experience. These experts have determined that the rules embody best practices and exhibit optimal robustness, making it more difficult for attackers to bypass Falco. These rules are highly relevant for addressing broader threats and are recommended for customization to specific environments if necessary. They primarily focus on universal system-level detections, such as generic reverse shells or container escapes, which establish a solid baseline for threat detection across diverse industries. This inherent bias against including more application-specific detections is due to their potential lack of broad relevance or applicability. However, to mitigate this bias, the maintainers reserve the right to make judgments on a case-by-case basis. +- **maturity_incubating** indicates that the rules address relevant threats, provide a certain level of robustness guarantee, and adhere to best practices in rule writing. Furthermore, it signifies that the rules have been identified by experts as catering to more specific use cases, which may or may not be relevant for each adopter. This category is expected to include a larger number of application-specific rules. +- **maturity_sandbox** indicates that the rule is in an experimental stage. The potential for broader usefulness and relevance of "sandbox" rules is currently being assessed. These rules can serve as inspiration and adhere to the minimum acceptance criteria for rules. +- **maturity_deprecated** indicates that, upon re-assessment, the rule was deemed less applicable to the broader community. Each adopter needs to determine the relevance of these rules on their own. They are kept as examples but are no longer actively supported or tuned by The Falco Project. + +In summary, the rules maturity tag reflects the robustness, relevance, applicability, and stability of each predefined rule in the [falcosecurity/rules](https://github.com/falcosecurity/rules/blob/main/rules/) repository. It serves as general guidance to determine which rules may provide the highest return on investment. + +## Justification of Rules Maturity Framework for Falco Adoption + +A rules maturity framework has been introduced for Falco users to facilitate the adoption of non-custom rules more effectively. This framework ensures a smooth transition for adopters, whether they use rules generically or for specific use cases. A smooth adoption process is defined by making it easy for adopters to understand each rule and also gain an understanding of not just what the rule is doing, but also how beneficial it can be under various circumstances. +Additionally, due to this framework, adopters should find themselves with a clearer understanding of which rules can likely be adopted as-is versus which rules may require significant engineering efforts to evaluate and adopt. + +The rules maturity framework aligns with the [status](https://github.com/falcosecurity/evolution/blob/main/REPOSITORIES.md#status) levels used within The Falco Project repositories, namely "Stable", "Incubating", "Sandbox" and "Deprecated". + +Not every rule has the potential to evolve and reach the "stable" level. This is because "stable" rules should address a broader range of attacks rather than being overly specific—such as detecting a single narrow CVE for a less common type of application, which could be easily bypassed. However, this does not mean that very specific rules do not provide value; on the contrary, they can serve a very specific purpose. These more specific rules may be better suited for custom adoption rather than integration into the upstream Falco rules. + +The new framework aims to help adopters easily identify the nature of a rule, whether it's more behavioral or signature-based. This is accomplished by providing clearer descriptions. You can explore this in more detail in the [Rules Overview Document](https://falcosecurity.github.io/rules/). + +The maturity level of the rules, however, does not directly reflect their potential for generating noise in the adopters' environment. This is due to the unique and constantly changing nature of each environment, especially in cloud environments, making it challenging to accurately predict the impact of rules. + +Newcomers to Falco are encouraged to start by configuring their setup with introductory rules labeled as `maturity_stable`. These rules, which are currently based on syscall and container events live in the established [falco_rules.yaml](https://github.com/falcosecurity/rules/blob/main/rules/falco_rules.yaml) file. + +As users become more familiar with Falco and better understand their unique environment, they can gradually fine-tune the rules to meet their specific requirements. Tuning rules goes hand in hand with assessing the performance overhead and adjusting Falco's [configuration](https://github.com/falcosecurity/falco/blob/master/falco.yaml) accordingly. This consideration is important to keep in mind as there are usually limitations to the budget allocated for security monitoring. + +Once adopters have integrated the stable default rules with low False Positives and acceptable performance overhead consistently, they can add a next set of rules. This set may include rules with `maturity_incubating` or `maturity_sandbox`, offering more specific detections and/or broader monitoring, depending on the rule. + +# Rules Acceptance Criteria + +The [maintainers](OWNERS) of this repository kindly reserve the right to make case-by-case decisions regarding rules acceptance and initial maturity leveling. + +The high-level principles that guide the review process for contributors and reviewers are as follows: + +- Each rule aligns with the project's best interests as per our [governance](https://github.com/falcosecurity/evolution/blob/main/GOVERNANCE.md). +- Each rule conforms to the [Style Guide of Falco Rules](https://falco.org/docs/rules/style-guide/). +- In particular, the [Rules Maturity Framework](#rules-maturity-framework) is honored. + +> Note: Any rule that would require using the `-A` flag (enabling high-volume syscalls) cannot be accepted beyond `maturity_sandbox` and `enabled: false` due to performance impact reasons. At the moment, we discourage upstream rules based on high-volume syscalls. However, this assessment may change as Falco evolves. + +*Correctness* + +As part of the review process, the following aspects will be thoroughly checked to effectively enforce the [Style Guide of Falco Rules](https://falco.org/docs/rules/style-guide/). + +- Correctness of the expression language, both syntactically and grammatically. +- Consistency with the name/description. +- If any tests are present, they must pass. During the initial review process and subsequent changes, manual testing should also be conducted to verify that the rule is capable of detecting the cyber threat(s) it aims to detect. In some cases, conducting more realistic tests, like deploying the rules on actual servers before acceptance, will be necessary. + +*Robustness* + +To enhance the effectiveness of detection, priority is given to behavioral detections, as opposed to string matching on process command arguments or other fields. This preference is based on the ease with which the latter can be circumvented. The same principle applies when selecting the most robust system call for detecting a specific threat. However, there is a place and purpose for more signature-based detections. The existing rules tagged with `maturity_stable` serve as a good starting point to explore a variety of useful rules that cover various attack vectors and employ both signature and behavior-based detection styles. Lastly, The Falco Project favors broader rules over narrow ones addressing a single, less common CVE for an application. + +*Relevance* + +Determining relevance is often the most subjective criterion, as it requires expertise in offensive security, cyber defense, and real-world production settings for accurate assessment. Questions such as whether these threats are a priority for most organizations or if we can provide enough context for a security analyst to appropriately act on the alerts as part of their incident response workflows are top of mind when assessing the overall relevance. Relevance is a key factor that indirectly reflects both robustness and significance, but more importantly, it indicates whether a particular security threat is significant to most adopters and, consequently, beneficial to broadly detect. + +Here are some aspects that can be discussed during the review process in order to decide if a rule has the potential to be effectively operationalized by most adopters: + +- Cover relevant attack vectors across various industries. +- Emphasize behavior-detection style + profiling over pure signatures (exceptions to this guideline apply). +- Evaluate the rule's effectiveness across diverse workloads (e.g. nodes serving web applications, databases, transactional processing, general compute or CI jobs). +- Guidance and templates to assist with tuning can be provided given Falco's current capabilities. diff --git a/OWNERS b/OWNERS index 2d8eb3d94..d0b38d53e 100644 --- a/OWNERS +++ b/OWNERS @@ -5,6 +5,7 @@ approvers: - fededp - andreagit97 - lucaguerra + - incertum reviewers: - leodido - kaizhe diff --git a/README.md b/README.md index b33b9e2e5..1e5d22e86 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,70 @@ # Falco Rules -[![License](https://img.shields.io/github/license/falcosecurity/rules?style=for-the-badge)](./LICENSE) +[![Latest release](https://img.shields.io/github/v/release/falcosecurity/rules?label=Latest%20Rules%20Release&style=for-the-badge)](https://github.com/falcosecurity/rules/releases/latest) [![Compatible Falco release](https://img.shields.io/github/v/release/falcosecurity/falco?label=Compatible%20Falco%20Release&style=for-the-badge)](https://github.com/falcosecurity/falco/releases/latest) -Note: *This repository has been created upon this [proposal](https://github.com/falcosecurity/falco/blob/master/proposals/20221129-artifacts-distribution.md#move-falco-rules-to-their-own-repo).* +[![Docs](https://img.shields.io/badge/docs-latest-green.svg?style=for-the-badge)](https://falco.org/docs/rules) [![Rules Overview](https://img.shields.io/badge/docs-latest-green.svg?label=Rules%20Overview&style=for-the-badge)](https://falcosecurity.github.io/rules/) [![Style Guide](https://img.shields.io/badge/docs-latest-green.svg?label=Style%20Guide&style=for-the-badge)](https://falco.org/docs/rules/style-guide/) -This repository contains the [Rules Files Registry](#registry) and *rules files* officially maintained by the Falcosecurity organization. [Rules](https://falco.org/docs/rules) tell [Falco](https://github.com/falcosecurity/falco) what to do. Please refer to the [official documentation](https://falco.org/docs/rules) to better understand the rules' concepts. +[![Supported Fields](https://img.shields.io/badge/docs-latest-green.svg?label=Supported%20Fields&style=for-the-badge)](https://falco.org/docs/reference/rules/supported-fields/) [![Supported EVT ARG Fields](https://img.shields.io/badge/docs-latest-green.svg?label=Supported%20Evt%20Arg%20Fields&style=for-the-badge)](https://github.com/falcosecurity/libs/blob/master/driver/event_table.c) -## Registry +[![Falco Core Repository](https://github.com/falcosecurity/evolution/blob/main/repos/badges/falco-core-blue.svg)](https://github.com/falcosecurity/evolution/blob/main/REPOSITORIES.md#core-scope) [![Stable](https://img.shields.io/badge/status-stable-brightgreen?style=for-the-badge)](https://github.com/falcosecurity/evolution/blob/main/REPOSITORIES.md#stable) [![License](https://img.shields.io/github/license/falcosecurity/rules?style=for-the-badge)](./LICENSE) [![Github Pages](https://github.com/falcosecurity/rules/actions/workflows/pages.yaml/badge.svg)](https://falcosecurity.github.io/rules/) -The Registry contains metadata and information about rules files distributed by the Falcosecurity organization. These rules are developed for Falco and made available to the community. +This repository has been created upon this [Proposal](https://github.com/falcosecurity/falco/blob/master/proposals/20221129-artifacts-distribution.md#move-falco-rules-to-their-own-repo) and contains the officially managed [Falco Rules](#falco-rules) by The Falco Project, along with the [Falco Rules Files Registry](#falco-rules-files-registry). -Note: _Currently, the registry includes only rules for the syscall call data source; for other data sources see the [plugins repository](https://github.com/falcosecurity/plugins)._ +## Falco Rules + +Rules tell [Falco](https://github.com/falcosecurity/falco) what to do. These rules are pre-defined detections for various security threats, abnormal behaviors, and compliance-related monitoring. + +
+ +

+ Image   + Explore the Official Documentation for a starting point and better understanding of rule concepts. Users can modify the community-contributed Falco rules to fit their needs or use them as examples. In most cases, users also create their own custom rules. Keep in mind that the rules in this repository are related to Falco's primary monitoring functions, specifically for syscalls and container events. Meanwhile, Falco plugin rules are stored within the respective subfolders of the Plugins repository.

+ + +

+ Image   + Because Falco rules, especially Sandbox and Incubating rules, are dynamic, it's crucial to stay updated. As threats and systems evolve, Falco evolves with each release. Therefore, regularly check the Rules Overview Document, Falco's Supported Fields, and Falco's release notes with every new release. It is recommended to consistently use the most recent Falco Release to avoid compatibility issues.

+ + +

+ Image   + Important: The Falco Project only guarantees that the most recent rules releases are compatible with the latest Falco release. Discover all rule files in the rules/ folder. Refer to our Release Process and Rules Maturity Framework for rule categorization, release procedures, and usage guidelines. Published upon tagging a new release, the maturity_stable rules in the falco_rules.yaml file are included in the Falco release package. Other maturity-level rules are released separately, requiring explicit installation and possible customization for effective Adoption.

+ + +

+ Image   + Beginning with rules version 3.0.0, the required_engine_version follows Semantic Versioning and requires Falco version 0.37.0 or higher. Since rules version 2.0.0, we've modified our rules' shipping and distribution process. With Falco >= 0.37.0, Selective Rules Overrides aim to further streamline the customization of rules. Since Falco 0.36.0, you can use the rule_matching config to resolve issues with rules overlapping, which is caused by the default "first match wins" principle. Starting from Falco 0.35.0, you have precise control over the syscalls that are being monitored, see base_syscalls. Lastly, keep in mind that the Rules Maturity Framework is a best effort on the part of the community, and ultimately, you have to decide if any rules are useful for your use cases.

+ + +

+ Image   + Be cautious: The main branch has the latest development. Before using rules from the main branch, check for compatibility. Changes like new output fields might cause incompatibilities with the latest stable Falco release. The Falco Project recommends using rules only from the release branches. Lastly, we'd like to highlight the importance of regular engineering effort to effectively adopt Falco rules. Considering that each adopter's system and monitoring needs are unique, it's advisable to view the rules as examples. +

+ +

+ Image   + Debugging: Historically, we've noted that issues often arise either from incorrect configurations or genuine bugs, acknowledging that no software is entirely bug-free. The Falco Project continually updates its Install and Operate and Troubleshooting guides. We kindly suggest reviewing these guides. In the context of Falco rules, missing fields, such as container images, may be anticipated within our imperfection tolerances under certain circumstances. We are committed to addressing and resolving issues within our control. +

+ +
+ +## Falco Rules Files Registry + +The Falco Rules Files Registry contains metadata and information about rules files distributed by The Falco Project. The registry serves as an additional method of making the rules files available to the community, complementing the process of retrieving the rules files from this repository. + +Note: _Currently, the registry includes only rules for the syscall call data source; for other data sources see the [Plugins](https://github.com/falcosecurity/plugins) repository._ + +### Naming Convention + +Rule files must be located in the [/rules](rules) folder of this repository and are named according to the following convention: `_rules.yaml`. + +The `` portion represents the _ruleset_ name, which must be an alphanumeric string, separated by `-`, entirely in lowercase, and beginning with a letter. + +Rule files are subsequently released using Git tags. The tag name should follow the pattern `-rules-`, where `` adheres to [Semantic Versioning](https://semver.org/). See [RELEASE](RELEASE.md) document for more details about our release process. + +For instance, the _falco_ ruleset is stored under [/rules/falco_rules.yaml](rules/falco_rules.yaml), and its version _1.0.0_ was released using the [falco-rules-1.0.0](https://github.com/falcosecurity/rules/releases/tag/falco-rules-1.0.0) tag. + +Note: _This convention applies to this repository only. Falco application does not impose any naming convention for naming rule files._ @@ -44,23 +98,43 @@ You can find the full registry specification here: *(coming soon...)* ### Registered Rules -Please refer to the automatically generated [rules_inventory/rules_overview.md](https://github.com/falcosecurity/rules/blob/main/rules_inventory/rules_overview.md#falco-rules---detailed-overview) file for a detailed list of all the rules currently registered. +Please refer to the automatically generated [rules overview](https://falcosecurity.github.io/rules/overview/) document file for a detailed list of all the rules currently registered. --> -## Hosted Rules +## Falco Rules 2.x + +Since version 2.0.0, the rules distributed from this repository have been split into three parts: -Another purpose of this repository is to host and maintain the rules owned by the Falcosecurity organization. All the rules are contained inside the [rules](https://github.com/falcosecurity/rules/tree/main/rules) folder. +- [Stable](https://github.com/falcosecurity/rules/blob/main/rules/falco_rules.yaml) Falco rules. Those are the only ones that are bundled in the Falco by default. It is very important to have a set of stable rules vetted by the community. To learn more about the criterias that are required for a rule to become stable, see the [Contributing](https://github.com/falcosecurity/rules/blob/main/CONTRIBUTING.md) guide. +- [Incubating](https://github.com/falcosecurity/rules/blob/main/rules/falco-incubating_rules.yaml) rules, which provide a certain level of robustness guarantee but have been identified by experts as catering to more specific use cases, which may or may not be relevant for each adopter. +- [Sandbox](https://github.com/falcosecurity/rules/blob/main/rules/falco-sandbox_rules.yaml) rules, which are more experimental. -The `main` branch contains the most up-to-date state of development. Please check our [Release Process](./RELEASE.md) to know how rules are released. Stable builds are released and published only when a new release gets tagged. +Previously, Falco used to bundle all the community rules in its default distribution. Today you can choose which set of rules you want to load in your distribution, depending on your preferred installation method: + +### Helm Chart + +If you are using the official Helm chart, you can add the incubating and/or sandbox repository in your [falcoctl](https://github.com/falcosecurity/charts/blob/f1062000e2e61332b3a8ea892a1765e4f4a60ec6/falco/values.yaml#L406) config and by enabling them in the corresponding `falco.yaml` file. + +For instance, in order to install the Helm chart and load all the available Falco rules with automatic update on all of them, you can run + +``` +helm install falco falcosecurity/falco --set "falcoctl.config.artifact.install.refs={falco-rules:2,falco-incubating-rules:2,falco-sandbox-rules:2}" --set "falcoctl.config.artifact.follow.refs={falco-rules:2,falco-incubating-rules:2,falco-sandbox-rules:2}" --set "falco.rules_file={/etc/falco/k8s_audit_rules.yaml,/etc/falco/rules.d,/etc/falco/falco_rules.yaml,/etc/falco/falco-incubating_rules.yaml,/etc/falco/falco-sandbox_rules.yaml}" +``` + +Where the option `falcoctl.config.artifact.install.refs` governs which rules are downloaded at startup, `falcoctl.config.artifact.follow.refs` identifies which rules are automatically updated and `falco.rules_file` indicates which rules are loaded by the engine. + +### Host installation + +If you are managing your Falco installation you should be aware of which directories contain the rules. Those are governed by the `rules_file` configuration option in your [falco.yaml](https://github.com/falcosecurity/falco/blob/ab6d76e6d2a076ca1403c91aa62213d2cadb73ea/falco.yaml#L146). Normally, there is also a `rules.d` directory that you can use to upload extra rules or you can add your custom files. + +Now you can simply download incubating or sandbox rules from the [rules](https://download.falco.org/?prefix=rules/) repository, uncompress and copy the file there. -If you wish to contribute your rules to the Falcosecurity organization, you just need to open a Pull Request to add them inside the `rules` folder. In order to be hosted in this repository, rules must be licensed under the [Apache 2.0 License](./LICENSE). ## Contributing -If you want to help and wish to contribute, please review our [contribution guidelines](https://github.com/falcosecurity/.github/blob/master/CONTRIBUTING.md). Code contributions are always encouraged and welcome! +If you are interested in helping and wish to contribute, we kindly request that you review our general [Contribution Guidelines](https://github.com/falcosecurity/.github/blob/master/CONTRIBUTING.md) and, more specifically, the dedicated [Rules Contributing](CONTRIBUTING.md) guide hosted in this repository. Please be aware that our reviewers will ensure compliance with the rules' acceptance criteria. ## License This project is licensed to you under the [Apache 2.0 Open Source License](./LICENSE). - diff --git a/RELEASE.md b/RELEASE.md index 57b92482c..d4170d319 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -6,30 +6,73 @@ Official Falcosecurity rules releases are automated using GitHub Actions. Each r In this repo, each ruleset is a standalone YAML file in the `/rules` directory (e.g. `falco_rules.yaml`, `application_rules.yaml`, ...). Each ruleset is released and versioned individually. When we release a ruleset, we do the following process: -1. Determine a new version for the given ruleset (see the [section below](#versioning-a-ruleset)) -2. Create a new Git tag with the name convention `*name*-rules-*version*` (e.g. `falco-rules-0.1.0`, `application-rules-0.1.0`, ...). The naming convention is required due to this repository being a [monorepo](https://en.wikipedia.org/wiki/Monorepo) and in order to be machine-readable. -3. A GitHub action will validate the repository [registry](./registry.yaml) and release the new OCI artifact in the packages of this repository +1. Make sure that the `./github/FALCO_VERSIONS` file contains the most recent versions of Falco with which the given ruleset validates successfully. + - When releasing a ruleset, the versions must be explicit stable Falco releases (e.g. not using `latest` or `master`), so that the new tag will provide an history of the Falco versions on which the ruleset was tested + - If the ruleset does not validate with a stable Falco release (it can happen close to the time of a Falco release), it's ok to use a `master` version, which however needs to be patched with an additional commit [on the ruleset's release branch](#patching-a-ruleset) once the next stable Falco release gets published. +2. Determine a new version for the given ruleset (see the [section below](#versioning-a-ruleset)). +3. Create a new Git tag with the name convention `*name*-rules-*version*` (e.g. `falco-rules-0.1.0`, `application-rules-0.1.0`, ...). The naming convention is required due to this repository being a [monorepo](https://en.wikipedia.org/wiki/Monorepo) and in order to be machine-readable. +4. A GitHub action will validate the repository [registry](./registry.yaml) and release the new OCI artifact in the packages of this repository. + +## Patching a ruleset + +Patches on an already-released ruleset can anytime on a per-need basis. Assuming a release Git tag in the form of `*name*-rules-*version*`, with version being in the form of `X.Y.Z` (e.g. `falco-rules-0.1.0`), the process is as follows: + +1. If not already created, create a release branch starting from the first tag of the released ruleset. + a. Checkout the first ruleset release tag: e.g. `git checkout falco-rules-0.1.0` + b. Create a new branch with the naming convention `release/*name*-rules-X.Y.x`, starting from the tag: e.g. `git checkout -b release/falco-rules-0.1.x` + c. As for step #1 of the [*Releasing a ruleset section*](#releasing-a-ruleset), make sure that `./github/FALCO_VERSIONS` contains all the **stable** Falco versions with which the ruleset validates successfully, by adding an extra commit to the release branch in case changes are required. + d. Open a [PR in falcosecurity/test-infra](https://github.com/falcosecurity/test-infra/edit/master/config/config.yaml) to add `release/*name*-rules-X.Y.x` as protected branch to the `prow/config.yaml`, for example: + ```yaml + rules: + branches: + main: + protect: true + ... + "release/falco-rules-0.1.x": + protect: true + ``` +2. Open one or more PRs agains the release branch of the released ruleset. + a. Wait for all the CI checks to pass. + b. Check the automatic versioning suggestions provided by the CI (see [an example](https://github.com/falcosecurity/rules/pull/74#issuecomment-1571867580)), and make sure that **only patch changes** are present. + c. Considering the automatic versioning suggestions, and at discretion of the domain knowledge of the repository maintainers, decide whether the PR should be merged or not. +3. Bump the ruleset version **patch** number by 1 and create a new Git tag from the release branch (e.g. `falco-rules-0.1.1`). +4. A GitHub action will validate the repository [registry](./registry.yaml) and release the new OCI artifact in the packages of this repository. ## Versioning a ruleset The version of the official Falco rulesets is compatible with [SemVer](https://semver.org/) and must be meaningful towards the changes in its structure and contents. To define a new version `x.y.z` for a given ruleset, consider the following guidelines. +Our automation will detect most of the following criteria and suggest a summary with all the changes relative to each of the three versioning categories (patch, minor, major). This provides a changelog and valuable suggestion on the next version to be assigned to each ruleset. However, be mindful that the versioning process cannot totally automated and always requires human attention (e.g. we can't automatically detect subtle semantic changes in rules). The automated jobs will use the versions of Falco defined in `./github/FALCO_VERSIONS` for validating and checking rulesets. The versions must be line-separated and ordered from the most recent to the least recent. Any [published container image tag](https://hub.docker.com/r/falcosecurity/falco/tags) is a valid Falco version entry, including `master`, `latest`, and any other stable release tag (e.g. `0.35.0`). `master` indicates the most recent dev version of Falco built from mainline, and can be used for using a not-yet-published version of Falco in case we want to run the CI with a new in-development feature. + **NOTE:** *The versioning guidelines also apply to any versioned ruleset not maintained in this repository (such as the ones in [falcosecurity/plugins](https://github.com/falcosecurity/plugins)), including the ones distributed by third parties. These are best practices that guarantee the correct behavior of Falco when updating a given ruleset to a new version.* - `z` _(patch number)_ is incremented when you make backward-compatible changes. In this case, the ruleset can be updated in a given Falco without needing to update Falco, its plugins, or its configuration. Examples: + - Decrementing `required_engine_version` + - Decrementing plugin version requirement in `required_plugin_versions` + - Adding alternatives entries to an already-existing plugin version requirement in `required_plugin_versions` + - Removing a plugin version requirement with all its alternatives in `required_plugin_versions` - Enabling at default one or more rules that used to be disabled - Adding or removing items for one or more lists - - Adding or removing exceptions for one or more Falco rules (without needing a `required_engine_version`) - - Changing the condition for one or more rules or macros by still preserving their logical security scope (e.g. making them less noisy) + - Adding one or more tags to a rule + - Increasing the priority of a rule + - Changing the output fields of a rule (without increasing `required_engine_version`) + - Adding or removing exceptions for one or more Falco rules (without increasing `required_engine_version`) + - Changing the condition for one or more rules by still preserving their logical security scope (e.g. making them less noisy, matching more events than before) - `y` _(minor number)_ is incremented when you add functionality in a backward-compatible manner. In order to be accepted, the new ruleset may mandate updating the version of Falco, changing its configuration, or updating/installing one or more plugins. Examples: - - Incrementing the `required_engine_version` number + - Incrementing `required_engine_version` - Incrementing the `required_plugin_versions` version requirement for one or more plugin - - Adding a new plugin version requirement in `required_plugin_versions` + - Adding a new plugin version requirement (with or without `alternatives`) in `required_plugin_versions` - Adding one or more lists, macros, or rules + - Adapting the ruleset to a Falco engine version introducing *backward-compatible* changes in the expected ruleset language definitions or file format. For now, this can't happen without also bumping `required_engine_version` since it is a simple progressive number. However, this may change in the future if we consider adopting a sem-ver-like version scheme. - `x` _(major number)_ is incremented when you make incompatible content changes which change the expected behavior and outcome of the ruleset. Incompatibilities may arise when relying on the ruleset from other rulesets (e.g. appending conditions or overriding the definition of a list, macro, or rule). Examples: + - Removing a plugin version requirement alternative (without removing the whole dependency) in `required_plugin_versions` - Renaming or removing a list, macro, or rule + - Changing the event source of a rule - Disabling at default one or more rules that used to be enabled - - Changing the logical security scope of one or more lists, macros, or rules (e.g. a rule stops detecting an entire spectrum of events that it used to detect, or starts serving a substantially different purpose) - - Adapting the ruleset to a Falco engine version introducing backward-incompatible changes in the expected ruleset language definitions or file format + - Removing one or more tags from a rule + - Decreasing the priority of a rule + - Changing in any way the set of matched events matched by a macro + - Changing the logical security scope of one or more macros or rules (e.g. a rule starts serving a substantially different purpose, or matches less events than before) + - Adapting the ruleset to a Falco engine version introducing *backward-incompatible* changes in the expected ruleset language definitions or file format When more than one version numbers need to be incremented, the most dominant takes precedence. For example, incrementing the `z` patch number would be enough when minorly chaning a rule's condition to make it less noisy, however you must increment the `y` minor number in case the new condition uses a new field or operator that requires increasing the `required_engine_version`. diff --git a/archive/README.md b/archive/README.md new file mode 100644 index 000000000..2c0e87fc2 --- /dev/null +++ b/archive/README.md @@ -0,0 +1,7 @@ +# Archived Rules Files + +This directory hosts archived rules files. While they are preserved for historical purposes or as examples, please note the following: + +- These files are **no longer maintained**. +- There's a possibility that they may **not function correctly**. +- They are **not recommended for use in production environments**. diff --git a/rules/application_rules.yaml b/archive/application_rules.yaml similarity index 99% rename from rules/application_rules.yaml rename to archive/application_rules.yaml index a95f9fb84..3d7b2afcf 100644 --- a/rules/application_rules.yaml +++ b/archive/application_rules.yaml @@ -1,5 +1,6 @@ +# SPDX-License-Identifier: Apache-2.0 # -# Copyright (C) 2019 The Falco Authors. +# Copyright (C) 2023 The Falco Authors. # # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/build/checker/.gitignore b/build/checker/.gitignore new file mode 100644 index 000000000..41646b19d --- /dev/null +++ b/build/checker/.gitignore @@ -0,0 +1 @@ +checker \ No newline at end of file diff --git a/build/checker/cmd/common.go b/build/checker/cmd/common.go new file mode 100644 index 000000000..ecbe9c825 --- /dev/null +++ b/build/checker/cmd/common.go @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2023 The Falco Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "fmt" + "math" + "strings" +) + +const defaultFalcoDockerImage = "falcosecurity/falco-no-driver:master" + +const defaultFalcoDockerEntrypoint = "/usr/bin/falco" + +var falcoPriorities = []string{ + "emergency", + "alert", + "critical", + "error", + "warning", + "notice", + "informational", // or "info" + "debug", +} + +// compareInt returns 1 if "left" is greater than right, +// -1 if "right" is greater than left, and 0 otherwise. +func compareInt(a, b int) int { + if a == b { + return 0 + } + if a < b { + return -1 + } + return 1 +} + +// compareFalcoPriorities returns 1 if "left" is more urgent than right, +// -1 if "right" is more urgent than left, and 0 otherwise. +func compareFalcoPriorities(left, right string) int { + lIndex := math.MaxInt + rIndex := math.MaxInt + for i, p := range falcoPriorities { + if strings.HasPrefix(p, strings.ToLower(left)) { + lIndex = i + } + if strings.HasPrefix(p, strings.ToLower(right)) { + rIndex = i + } + } + return compareInt(rIndex, lIndex) +} + +// errAppend returns an error resulting froma appending two errors. +func errAppend(left, right error) error { + if left == nil { + return right + } + if right == nil { + return left + } + return fmt.Errorf("%s, %s", left.Error(), right.Error()) +} + +// strSliceToMap returns a map[string]bool (a set, basically) from a strings slice. +func strSliceToMap(s []string) map[string]bool { + items := make(map[string]bool) + for _, item := range s { + items[item] = true + } + return items +} + +// diffStrSet returns a map[string]bool containing all the strings present +// in left but without the strings present in right. +func diffStrSet(left, right []string) map[string]bool { + l := strSliceToMap(left) + r := strSliceToMap(right) + for k := range r { + delete(l, k) + } + return l +} diff --git a/build/checker/cmd/common_test.go b/build/checker/cmd/common_test.go new file mode 100644 index 000000000..16fa4661e --- /dev/null +++ b/build/checker/cmd/common_test.go @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2023 The Falco Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCompareInt(t *testing.T) { + t.Parallel() + assert.Equal(t, -1, compareInt(1, 2)) + assert.Equal(t, -1, compareInt(1, 5)) + assert.Equal(t, 0, compareInt(5, 5)) + assert.Equal(t, 1, compareInt(6, 5)) + assert.Equal(t, 1, compareInt(10, 5)) +} + +func TestCompareFalcoPriorities(t *testing.T) { + t.Parallel() + assert.Equal(t, -1, compareFalcoPriorities("debug", "info")) + assert.Equal(t, 1, compareFalcoPriorities("info", "debug")) + assert.Equal(t, 0, compareFalcoPriorities("info", "informational")) +} + +func TestDiffStrSet(t *testing.T) { + t.Parallel() + a := []string{"a", "b", "c"} + b := []string{"b", "d"} + + d1 := diffStrSet(a, b) + assert.Len(t, d1, 2) + assert.Contains(t, d1, "a") + assert.Contains(t, d1, "c") + + d2 := diffStrSet(b, a) + assert.Len(t, d2, 1) + assert.Contains(t, d2, "d") +} diff --git a/build/checker/cmd/compare.go b/build/checker/cmd/compare.go new file mode 100644 index 000000000..3490fa345 --- /dev/null +++ b/build/checker/cmd/compare.go @@ -0,0 +1,468 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2023 The Falco Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "encoding/json" + "fmt" + "strconv" + + "github.com/blang/semver" + "github.com/falcosecurity/testing/pkg/falco" + "github.com/falcosecurity/testing/pkg/run" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +func getVerRequirement(f *falco.PluginVersionRequirementDescription, pluginName string) *falco.PluginVersionRequirement { + if f.Name == pluginName { + return &f.PluginVersionRequirement + } + for _, a := range f.Alternatives { + if a.Name == pluginName { + return &a + } + } + return nil +} + +func getRequirements(f *falco.PluginVersionRequirementDescription) []*falco.PluginVersionRequirement { + var res []*falco.PluginVersionRequirement + res = append(res, &f.PluginVersionRequirement) + for _, a := range f.Alternatives { + res = append(res, &a) + } + return res +} + +func findPluginVerRequirement(f *falco.RulesetDescription, pluginName string) *falco.PluginVersionRequirementDescription { + for _, r := range f.RequiredPluginVersions { + req := getVerRequirement(&r, pluginName) + if req != nil { + return &r + } + } + return nil +} + +func listNames(f *falco.RulesetDescription) []string { + var names []string + for _, l := range f.Lists { + names = append(names, l.Info.Name) + } + return names +} + +func macroNames(f *falco.RulesetDescription) []string { + var names []string + for _, l := range f.Macros { + names = append(names, l.Info.Name) + } + return names +} + +func ruleNames(f *falco.RulesetDescription) []string { + var names []string + for _, l := range f.Rules { + names = append(names, l.Info.Name) + } + return names +} + +func getCompareOutput(falcoImage, configFile string, ruleFiles, extraFiles []string) (*falco.RulesetDescription, error) { + testOptions := []falco.TestOption{ + falco.WithOutputJSON(), + falco.WithOutputJSON(), + falco.WithArgs("-L"), + } + + for _, rf := range ruleFiles { + f := run.NewLocalFileAccessor(rf, rf) + testOptions = append(testOptions, falco.WithRules(f)) + } + + if len(configFile) > 0 { + f := run.NewLocalFileAccessor(configFile, configFile) + testOptions = append(testOptions, falco.WithConfig(f)) + } + + for _, ef := range extraFiles { + f := run.NewLocalFileAccessor(ef, ef) + testOptions = append(testOptions, falco.WithExtraFiles(f)) + } + + // run falco and collect/print validation issues + runner, err := run.NewDockerRunner(falcoImage, defaultFalcoDockerEntrypoint, nil) + if err != nil { + return nil, err + } + + res := falco.Test(runner, testOptions...) + + // collect errors + err = errAppend(err, res.Err()) + if res.ExitCode() != 0 { + err = errAppend(err, fmt.Errorf("unexpected exit code (%d)", res.ExitCode())) + } + + // unmarshal json output + var out falco.RulesetDescription + err = json.Unmarshal(([]byte)(res.Stdout()), &out) + if err != nil { + logrus.Info(res.Stderr()) + return nil, err + } + + if err != nil { + return nil, err + } + return &out, nil +} + +func compareRulesPatch(left, right *falco.RulesetDescription) (res []string) { + // Decrementing required_engine_version + lRequiredEngineVersion, _ := strconv.Atoi(left.RequiredEngineVersion) + rRequiredEngineVersion, _ := strconv.Atoi(right.RequiredEngineVersion) + if compareInt(lRequiredEngineVersion, rRequiredEngineVersion) > 0 { + res = append(res, fmt.Sprintf("Required engine version was decremented from %s to %s", + left.RequiredEngineVersion, right.RequiredEngineVersion)) + } + + // Remove or decrement plugin version requirement + for _, lpr := range left.RequiredPluginVersions { + var tmpRemoveRes []string + lpReqs := getRequirements(&lpr) + for _, lr := range lpReqs { + rr := findPluginVerRequirement(right, lr.Name) + if rr == nil { + // removed dep (not an alternative) + tmpRemoveRes = append(tmpRemoveRes, fmt.Sprintf("Version dependency to plugin `%s` has removed", lr.Name)) + } else { + // decremented + lv := semver.MustParse(lr.Version) + rv := semver.MustParse(getVerRequirement(rr, lr.Name).Version) + if lv.Compare(rv) > 0 { + res = append(res, fmt.Sprintf("Version dependency to plugin `%s` has been decremented", lr.Name)) + } + } + } + if len(tmpRemoveRes) == len(lpReqs) { + res = append(res, tmpRemoveRes...) + } + } + + // Adding plugin version requirement alternative + for _, rpr := range right.RequiredPluginVersions { + var lrl *falco.PluginVersionRequirementDescription + rReqs := getRequirements(&rpr) + for _, rreq := range rReqs { + lrl = findPluginVerRequirement(left, rreq.Name) + if lrl != nil { + break + } + } + if lrl != nil { + for _, rreq := range rReqs { + if getVerRequirement(lrl, rreq.Name) == nil { + res = append(res, fmt.Sprintf("Version dependency alternative to plugin `%s` has added", rreq.Name)) + } + } + } + } + + for _, l := range left.Rules { + for _, r := range right.Rules { + if l.Info.Name == r.Info.Name { + // Enabling at default one or more rules that used to be disabled + if !l.Info.Enabled && r.Info.Enabled { + res = append(res, fmt.Sprintf("Rule `%s` has been enabled at default", l.Info.Name)) + } + + // Matching more events in a rule condition + if len(diffStrSet(r.Details.Events, l.Details.Events)) > 0 { + res = append(res, fmt.Sprintf("Rule `%s` matches more events than before", l.Info.Name)) + } + + // A rule has different output fields + if compareInt(len(l.Details.OutputFields), len(r.Details.OutputFields)) != 0 { + res = append(res, fmt.Sprintf("Rule `%s` changed its output fields", l.Info.Name)) + } + + // A rule has more tags than before + if len(diffStrSet(r.Info.Tags, l.Info.Tags)) > 0 { + res = append(res, fmt.Sprintf("Rule `%s` has more tags than before", l.Info.Name)) + } + + // A rule's priority becomes more urgent than before + if compareFalcoPriorities(r.Info.Priority, l.Info.Priority) > 0 { + res = append(res, fmt.Sprintf("Rule `%s` has a more urgent priority than before", l.Info.Name)) + } + + // Adding or removing exceptions for one or more Falco rules + if len(diffStrSet(l.Details.ExceptionNames, r.Details.ExceptionNames)) != 0 || + len(diffStrSet(r.Details.ExceptionNames, l.Details.ExceptionNames)) != 0 { + res = append(res, fmt.Sprintf("Rule '%s' has some exceptions added or removed", l.Info.Name)) + } + + } + } + } + + for _, l := range left.Lists { + for _, r := range right.Lists { + if l.Info.Name == r.Info.Name { + // Adding or removing items for one or more lists + if len(diffStrSet(l.Info.Items, r.Info.Items)) != 0 || + len(diffStrSet(r.Info.Items, l.Info.Items)) != 0 { + res = append(res, fmt.Sprintf("List `%s` has some item added or removed", l.Info.Name)) + } + } + } + } + + return +} + +func compareRulesMinor(left, right *falco.RulesetDescription) (res []string) { + // Incrementing the required_engine_version number + l_required_engine_version, _ := strconv.Atoi(left.RequiredEngineVersion) + r_required_engine_version, _ := strconv.Atoi(right.RequiredEngineVersion) + if compareInt(l_required_engine_version, r_required_engine_version) < 0 { + res = append(res, fmt.Sprintf("Required engine version was incremented from %s to %s", + left.RequiredEngineVersion, right.RequiredEngineVersion)) + } + + // Adding a new plugin version requirement in required_plugin_versions + for _, rpr := range right.RequiredPluginVersions { + var lrl *falco.PluginVersionRequirementDescription + rReqs := getRequirements(&rpr) + for _, rreq := range rReqs { + lrl = findPluginVerRequirement(left, rreq.Name) + if lrl != nil { + break + } + } + if lrl == nil { + res = append(res, fmt.Sprintf("Version dependency to plugin `%s` has added", rpr.Name)) + } + } + + // Incrementing the version requirement for one or more plugin + for _, lpr := range left.RequiredPluginVersions { + lpReqs := getRequirements(&lpr) + for _, lr := range lpReqs { + rr := findPluginVerRequirement(right, lr.Name) + if rr != nil { + lv := semver.MustParse(lr.Version) + rv := semver.MustParse(getVerRequirement(rr, lr.Name).Version) + if lv.Compare(rv) < 0 { + res = append(res, fmt.Sprintf("Version dependency to plugin `%s` has been incremented", lr.Name)) + } + } + } + } + + // Adding one or more lists, macros, or rules + diff := diffStrSet(ruleNames(right), ruleNames(left)) + if len(diff) > 0 { + for v := range diff { + res = append(res, fmt.Sprintf("Rule `%s` has been added", v)) + } + } + diff = diffStrSet(macroNames(right), macroNames(left)) + if len(diff) > 0 { + for v := range diff { + res = append(res, fmt.Sprintf("Macro `%s` has been added", v)) + } + } + diff = diffStrSet(listNames(right), listNames(left)) + if len(diff) > 0 { + for v := range diff { + res = append(res, fmt.Sprintf("List `%s` has been added", v)) + } + } + + return +} + +func compareRulesMajor(left, right *falco.RulesetDescription) (res []string) { + // Remove plugin version requirement alternative + for _, lpr := range left.RequiredPluginVersions { + var tmpRes []string + lpReqs := getRequirements(&lpr) + for _, lr := range lpReqs { + rr := findPluginVerRequirement(right, lr.Name) + if rr == nil && len(lpr.Alternatives) > 0 { + // removed dep (an alternative) + tmpRes = append(tmpRes, fmt.Sprintf("Version dependency alternative to plugin `%s` has removed", lr.Name)) + } + } + // it's not a breaking change to remove a whole plugin dependency block + if len(tmpRes) < len(lpReqs) { + res = append(res, tmpRes...) + } + } + + // Renaming or removing a list, macro, or rule + diff := diffStrSet(ruleNames(left), ruleNames(right)) + if len(diff) > 0 { + for v := range diff { + res = append(res, fmt.Sprintf("Rule `%s` has been removed", v)) + } + } + diff = diffStrSet(macroNames(left), macroNames(right)) + if len(diff) > 0 { + for v := range diff { + res = append(res, fmt.Sprintf("Macro `%s` has been removed", v)) + } + } + diff = diffStrSet(listNames(left), listNames(right)) + if len(diff) > 0 { + for v := range diff { + res = append(res, fmt.Sprintf("List `%s` has been removed", v)) + } + } + + for _, l := range left.Rules { + for _, r := range right.Rules { + if l.Info.Name == r.Info.Name { + // Rule has a different source + if l.Info.Source != r.Info.Source { + res = append(res, fmt.Sprintf("Rule `%s` has different source (before='%s', after='%s')", l.Info.Name, l.Info.Source, r.Info.Source)) + } + + // Disabling at default one or more rules that used to be enabled + if l.Info.Enabled && !r.Info.Enabled { + res = append(res, fmt.Sprintf("Rule `%s` has been disabled at default", l.Info.Name)) + } + + // Matching less events in a rule condition + if len(diffStrSet(l.Details.Events, r.Details.Events)) > 0 { + res = append(res, fmt.Sprintf("Rule `%s` matches less events than before", l.Info.Name)) + } + + // A rule has less tags than before + if len(diffStrSet(l.Info.Tags, r.Info.Tags)) > 0 { + res = append(res, fmt.Sprintf("Rule `%s` has less tags than before", l.Info.Name)) + } + + // a priority becomes less urgent than before + if compareFalcoPriorities(l.Info.Priority, r.Info.Priority) > 0 { + res = append(res, fmt.Sprintf("Rule `%s` has a less urgent priority than before", l.Info.Name)) + } + } + } + } + + for _, l := range left.Macros { + for _, r := range right.Macros { + if l.Info.Name == r.Info.Name { + // Matching different events in a macro condition + if len(diffStrSet(l.Details.Events, r.Details.Events)) > 0 || + len(diffStrSet(r.Details.Events, l.Details.Events)) > 0 { + res = append(res, fmt.Sprintf("Macro `%s` matches different events than before", l.Info.Name)) + } + } + } + } + return +} + +var compareCmd = &cobra.Command{ + Use: "compare", + // todo: load more than one rules files both on left and right + Short: "Compare two rules files and suggest version changes", + RunE: func(cmd *cobra.Command, args []string) error { + leftRules, err := cmd.Flags().GetStringArray("left") + if err != nil { + return err + } + + rightRules, err := cmd.Flags().GetStringArray("right") + if err != nil { + return err + } + + if len(leftRules) == 0 || len(rightRules) == 0 { + return fmt.Errorf("you must specify at least one rules file for both the left-hand and right-hand sides of comparison") + } + + falcoImage, err := cmd.Flags().GetString("falco-image") + if err != nil { + return err + } + + falcoConfigPath, err := cmd.Flags().GetString("config") + if err != nil { + return err + } + + falcoFilesPaths, err := cmd.Flags().GetStringArray("file") + if err != nil { + return err + } + + leftOutput, err := getCompareOutput(falcoImage, falcoConfigPath, leftRules, falcoFilesPaths) + if err != nil { + return err + } + + rightOutput, err := getCompareOutput(falcoImage, falcoConfigPath, rightRules, falcoFilesPaths) + if err != nil { + return err + } + + diff := compareRulesMajor(leftOutput, rightOutput) + if len(diff) > 0 { + fmt.Fprintln(cmd.OutOrStdout(), "**Major** changes:") + for _, s := range diff { + fmt.Fprintln(cmd.OutOrStdout(), "* "+s) + } + fmt.Fprintln(cmd.OutOrStdout()) + } + + diff = compareRulesMinor(leftOutput, rightOutput) + if len(diff) > 0 { + fmt.Fprintln(cmd.OutOrStdout(), "**Minor** changes:") + for _, s := range diff { + fmt.Fprintln(cmd.OutOrStdout(), "* "+s) + } + fmt.Fprintln(cmd.OutOrStdout()) + } + + diff = compareRulesPatch(leftOutput, rightOutput) + if len(diff) > 0 { + fmt.Fprintln(cmd.OutOrStdout(), "**Patch** changes:") + for _, s := range diff { + fmt.Fprintln(cmd.OutOrStdout(), "* "+s) + } + fmt.Fprintln(cmd.OutOrStdout()) + } + + return nil + }, +} + +func init() { + compareCmd.Flags().StringP("falco-image", "i", defaultFalcoDockerImage, "Docker image of Falco to be used for validation") + compareCmd.Flags().StringP("config", "c", "", "Config file to be used for running Falco") + compareCmd.Flags().StringArrayP("file", "f", []string{}, "Extra files required by Falco for running") + compareCmd.Flags().StringArrayP("left", "l", []string{}, "Rules files to be loaded for the left-hand side of the comparison") + compareCmd.Flags().StringArrayP("right", "r", []string{}, "Rules files to be loaded for the right-hand side of the comparison") + rootCmd.AddCommand(compareCmd) +} diff --git a/build/checker/cmd/compare_test.go b/build/checker/cmd/compare_test.go new file mode 100644 index 000000000..4ef11016a --- /dev/null +++ b/build/checker/cmd/compare_test.go @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2023 The Falco Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "encoding/json" + "testing" + + "github.com/falcosecurity/testing/pkg/falco" + "github.com/stretchr/testify/assert" +) + +const sampleFalcoCompareOutput = `{ + "lists": [ + { + "details": { + "lists": [] + }, + "info": { + "items": ["ash", "bash"], + "name": "list1" + } + } + ], + "macros": [ + { + "details": { + "condition_fields": ["fd.num","evt.type"], + "events": ["openat2","openat","open"], + "lists": [], + "macros": [], + "operators": [">=","=","in"] + }, + "info": { + "name": "macro1" + } + } + ], + "required_engine_version": "13", + "required_plugin_versions": [ + { + "alternatives": [ + { + "name": "k8saudit-eks", + "version": "0.2.0" + } + ], + "name": "k8saudit", + "version": "0.6.0" + }, + { + "name": "json", + "version": "0.7.0" + } + ], + "rules": [ + { + "details": { + "condition_fields": [], + "events": ["execve", "openat"], + "exceptions" : [], + "exception_fields": [], + "exception_operators": [], + "lists": [], + "macros": [], + "operators": [], + "output_fields": ["user.name","container.id"] + }, + "info": { + "enabled": false, + "name": "rule1", + "priority": "Notice", + "source": "syscall", + "tags": ["container","network"] + } + } + ] + }` + +func testGetSampleFalcoCompareOutput(t *testing.T) *falco.RulesetDescription { + var out falco.RulesetDescription + err := json.Unmarshal(([]byte)(sampleFalcoCompareOutput), &out) + if err != nil { + t.Fatal(err.Error()) + } + return &out +} + +func TestCompareRulesPatch(t *testing.T) { + t.Parallel() + + t.Run("decrement-required-engine-version", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.RequiredEngineVersion = "0" + res := compareRulesPatch(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + + t.Run("remove-plugin-version-requirement", func(t *testing.T) { + t.Parallel() + t.Run("with-alternatives", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.RequiredPluginVersions = o2.RequiredPluginVersions[1:] + res := compareRulesPatch(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 2) + }) + t.Run("with-no-alternatives", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.RequiredPluginVersions = o2.RequiredPluginVersions[:1] + res := compareRulesPatch(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + }) + + t.Run("add-plugin-version-requirement-alternative", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + a := falco.PluginVersionRequirement{Name: "json2", Version: "0.1.0"} + o2.RequiredPluginVersions[1].Alternatives = append(o2.RequiredPluginVersions[1].Alternatives, a) + res := compareRulesPatch(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + + t.Run("change-list", func(t *testing.T) { + t.Parallel() + t.Run("add-item", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.Lists[0].Info.Items = append(o2.Lists[0].Info.Items, "some_value") + res := compareRulesPatch(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + t.Run("remove-item", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.Lists[0].Info.Items = []string{} + res := compareRulesPatch(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + }) + + t.Run("change-rule", func(t *testing.T) { + t.Parallel() + t.Run("enable", func(t *testing.T) { + t.Parallel() + o1 := testGetSampleFalcoCompareOutput(t) + o2 := testGetSampleFalcoCompareOutput(t) + o1.Rules[0].Info.Enabled = false + o2.Rules[0].Info.Enabled = true + res := compareRulesPatch(o1, o2) + assert.Len(t, res, 1) + }) + t.Run("add-events", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.Rules[0].Details.Events = append(o2.Rules[0].Details.Events, "pluginevent") + res := compareRulesPatch(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + t.Run("add-tags", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.Rules[0].Info.Tags = append(o2.Rules[0].Info.Tags, "some_other_tag") + res := compareRulesPatch(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + t.Run("remove-output-field", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.Rules[0].Details.OutputFields = []string{} + res := compareRulesPatch(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + t.Run("add-output-field", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.Rules[0].Details.OutputFields = append(o2.Rules[0].Details.OutputFields, "some.otherfield") + res := compareRulesPatch(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + t.Run("greater-priority", func(t *testing.T) { + t.Parallel() + o1 := testGetSampleFalcoCompareOutput(t) + o2 := testGetSampleFalcoCompareOutput(t) + o1.Rules[0].Info.Priority = "DEBUG" + o2.Rules[0].Info.Priority = "INFO" + res := compareRulesPatch(o1, o2) + assert.Len(t, res, 1) + }) + t.Run("add-exceptions", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.Rules[0].Details.ExceptionNames = append(o2.Rules[0].Details.ExceptionNames, "some-exception-name") + res := compareRulesPatch(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + t.Run("remove-exceptions", func(t *testing.T) { + t.Parallel() + o1 := testGetSampleFalcoCompareOutput(t) + o2 := testGetSampleFalcoCompareOutput(t) + o1.Rules[0].Details.ExceptionNames = append(o1.Rules[0].Details.ExceptionNames, "exception1, exception2") + o2.Rules[0].Details.ExceptionNames = append(o2.Rules[0].Details.ExceptionNames, "exception1") + res := compareRulesPatch(o1, o2) + assert.Len(t, res, 1) + }) + }) +} + +func TestCompareRulesMinor(t *testing.T) { + t.Parallel() + + t.Run("increment-required-engine-version", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.RequiredEngineVersion = "100" + res := compareRulesMinor(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + + t.Run("add-plugin-version-requirement", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + dep := falco.PluginVersionRequirementDescription{ + PluginVersionRequirement: falco.PluginVersionRequirement{Name: "some_other_plugin", Version: "0.1.0"}, + } + o2.RequiredPluginVersions = append(o2.RequiredPluginVersions, dep) + res := compareRulesMinor(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + + t.Run("increment-plugin-version-requirement", func(t *testing.T) { + t.Parallel() + t.Run("of alternative", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.RequiredPluginVersions[0].Alternatives[0].Version = "10.0.0" + res := compareRulesMinor(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + t.Run("of main requirement", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.RequiredPluginVersions[1].Version = "10.0.0" + res := compareRulesMinor(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + }) + + t.Run("add-list", func(t *testing.T) { + t.Parallel() + l := falco.ListDescription{} + l.Info.Name = "l2" + o2 := testGetSampleFalcoCompareOutput(t) + o2.Lists = append(o2.Lists, l) + res := compareRulesMinor(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + + t.Run("add-macro", func(t *testing.T) { + t.Parallel() + l := falco.MacroDescription{} + l.Info.Name = "m2" + o2 := testGetSampleFalcoCompareOutput(t) + o2.Macros = append(o2.Macros, l) + res := compareRulesMinor(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + + t.Run("add-rule", func(t *testing.T) { + t.Parallel() + l := falco.RuleDescription{} + l.Info.Name = "r2" + o2 := testGetSampleFalcoCompareOutput(t) + o2.Rules = append(o2.Rules, l) + res := compareRulesMinor(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + + t.Run("add-all", func(t *testing.T) { + t.Parallel() + l := falco.ListDescription{} + l.Info.Name = "l2" + m := falco.MacroDescription{} + m.Info.Name = "m2" + r := falco.RuleDescription{} + r.Info.Name = "r2" + o2 := testGetSampleFalcoCompareOutput(t) + o2.Lists = append(o2.Lists, l) + o2.Macros = append(o2.Macros, m) + o2.Rules = append(o2.Rules, r) + res := compareRulesMinor(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 3) + }) +} + +func TestCompareRulesMajor(t *testing.T) { + t.Parallel() + + t.Run("remove-plugin-version-requirement", func(t *testing.T) { + t.Parallel() + t.Run("with-alternatives", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.RequiredPluginVersions[0].Alternatives = []falco.PluginVersionRequirement{} + res := compareRulesMajor(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + }) + + t.Run("remove-list", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.Lists = []falco.ListDescription{} + res := compareRulesMajor(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + + t.Run("remove-macro", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.Macros = []falco.MacroDescription{} + res := compareRulesMajor(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + + t.Run("remove-rule", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.Rules = []falco.RuleDescription{} + res := compareRulesMajor(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + + t.Run("remove-all", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.Lists = []falco.ListDescription{} + o2.Macros = []falco.MacroDescription{} + o2.Rules = []falco.RuleDescription{} + res := compareRulesMajor(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 3) + }) + + t.Run("change-macro", func(t *testing.T) { + t.Parallel() + t.Run("add-events", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.Macros[0].Details.Events = append(o2.Macros[0].Details.Events, "pluginevent") + res := compareRulesMajor(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + t.Run("remove-events", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.Macros[0].Details.Events = []string{} + res := compareRulesMajor(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + }) + + t.Run("change-rule", func(t *testing.T) { + t.Parallel() + t.Run("change-source", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.Rules[0].Info.Source = "some_other_source" + res := compareRulesMajor(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + t.Run("disable", func(t *testing.T) { + t.Parallel() + o1 := testGetSampleFalcoCompareOutput(t) + o2 := testGetSampleFalcoCompareOutput(t) + o1.Rules[0].Info.Enabled = true + o2.Rules[0].Info.Enabled = false + res := compareRulesMajor(o1, o2) + assert.Len(t, res, 1) + }) + t.Run("remove-events", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.Rules[0].Details.Events = []string{} + res := compareRulesMajor(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + t.Run("remove-tags", func(t *testing.T) { + t.Parallel() + o2 := testGetSampleFalcoCompareOutput(t) + o2.Rules[0].Info.Tags = []string{} + res := compareRulesMajor(testGetSampleFalcoCompareOutput(t), o2) + assert.Len(t, res, 1) + }) + t.Run("lower-priority", func(t *testing.T) { + t.Parallel() + o1 := testGetSampleFalcoCompareOutput(t) + o2 := testGetSampleFalcoCompareOutput(t) + o1.Rules[0].Info.Priority = "INFO" + o2.Rules[0].Info.Priority = "DEBUG" + res := compareRulesMajor(o1, o2) + assert.Len(t, res, 1) + }) + }) +} diff --git a/build/checker/cmd/root.go b/build/checker/cmd/root.go new file mode 100644 index 000000000..799a6e294 --- /dev/null +++ b/build/checker/cmd/root.go @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2023 The Falco Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var rootCmd = &cobra.Command{ + Use: "checker", + Short: "General-purpose tool for sanity checks around Falco rules files", + Long: "", +} + +// Execute adds all child commands to the root command. +func Execute() { + logrus.SetLevel(logrus.DebugLevel) + cobra.CheckErr(rootCmd.Execute()) +} diff --git a/build/checker/cmd/validate.go b/build/checker/cmd/validate.go new file mode 100644 index 000000000..d60751165 --- /dev/null +++ b/build/checker/cmd/validate.go @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2023 The Falco Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "fmt" + + "github.com/falcosecurity/testing/pkg/falco" + "github.com/falcosecurity/testing/pkg/run" + "github.com/spf13/cobra" +) + +var validateCmd = &cobra.Command{ + Use: "validate", + Short: "Validate one or more rules file with a given Falco version", + RunE: func(cmd *cobra.Command, args []string) error { + falcoImage, err := cmd.Flags().GetString("falco-image") + if err != nil { + return err + } + + rulesFilesPaths, err := cmd.Flags().GetStringArray("rule") + if err != nil { + return err + } + + if len(rulesFilesPaths) == 0 { + return fmt.Errorf("you must specify at least one rules file") + } + + var ruleFiles []run.FileAccessor + for _, rf := range rulesFilesPaths { + f := run.NewLocalFileAccessor(rf, rf) + ruleFiles = append(ruleFiles, f) + } + + falcoTestOptions := []falco.TestOption{ + falco.WithOutputJSON(), + falco.WithRulesValidation(ruleFiles...), + } + + falcoConfigPath, err := cmd.Flags().GetString("config") + if err != nil { + return err + } + if len(falcoConfigPath) > 0 { + config := run.NewLocalFileAccessor(falcoConfigPath, falcoConfigPath) + falcoTestOptions = append(falcoTestOptions, falco.WithConfig(config)) + } + + falcoFilesPaths, err := cmd.Flags().GetStringArray("file") + if err != nil { + return err + } + if len(falcoFilesPaths) > 0 { + for _, path := range falcoFilesPaths { + file := run.NewLocalFileAccessor(path, path) + falcoTestOptions = append(falcoTestOptions, falco.WithExtraFiles(file)) + } + } + + // run falco and collect/print validation issues + runner, err := run.NewDockerRunner(falcoImage, defaultFalcoDockerEntrypoint, nil) + if err != nil { + return err + } + + res := falco.Test(runner, falcoTestOptions...) + if res.RuleValidation() == nil { + err = errAppend(err, fmt.Errorf("rules validation command failed")) + } else { + for _, r := range res.RuleValidation().Results { + if !r.Successful || len(r.Errors) > 0 || len(r.Warnings) > 0 { + err = errAppend(err, fmt.Errorf("rules validation had warning or errors")) + fmt.Fprintln(cmd.OutOrStdout(), res.Stdout()) + break + } + } + } + + // collect errors + err = errAppend(err, res.Err()) + if res.ExitCode() != 0 { + err = errAppend(err, fmt.Errorf("unexpected exit code (%d)", res.ExitCode())) + } + if err != nil { + fmt.Fprintln(cmd.ErrOrStderr(), res.Stderr()) + } + return err + }, +} + +func init() { + validateCmd.Flags().StringP("falco-image", "i", defaultFalcoDockerImage, "Docker image of Falco to be used for validation") + validateCmd.Flags().StringP("config", "c", "", "Config file to be used for running Falco") + validateCmd.Flags().StringArrayP("file", "f", []string{}, "Extra files required by Falco for running") + validateCmd.Flags().StringArrayP("rule", "r", []string{}, "Rules files to be validated by Falco") + rootCmd.AddCommand(validateCmd) +} diff --git a/build/checker/go.mod b/build/checker/go.mod new file mode 100644 index 000000000..806f0d3ea --- /dev/null +++ b/build/checker/go.mod @@ -0,0 +1,35 @@ +module checker + +go 1.19 + +require ( + github.com/blang/semver v3.5.1+incompatible + github.com/falcosecurity/testing v0.0.0-20230929141322-214d1fbb9327 + github.com/sirupsen/logrus v1.9.0 + github.com/spf13/cobra v1.7.0 + github.com/stretchr/testify v1.8.3 +) + +require ( + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect + github.com/Microsoft/go-winio v0.6.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/docker/distribution v2.8.1+incompatible // indirect + github.com/docker/docker v24.0.3+incompatible // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.0.2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect + golang.org/x/net v0.7.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/tools v0.1.12 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/build/checker/go.sum b/build/checker/go.sum new file mode 100644 index 000000000..be6cc98f6 --- /dev/null +++ b/build/checker/go.sum @@ -0,0 +1,98 @@ +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= +github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= +github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= +github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.3+incompatible h1:Kz/tcUmXhIojEivEoPcRWzL01tVRek7Th15/8BsRPWw= +github.com/docker/docker v24.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/falcosecurity/testing v0.0.0-20230929122211-ffc9e0c14c6b h1:4sFusgDWtUSTVa9ZY/BjZThNUdUp6m3QEPpzNfZGWVM= +github.com/falcosecurity/testing v0.0.0-20230929122211-ffc9e0c14c6b/go.mod h1:yJVIonjfOTCI7yFQsJRU1PRFL57Ddad0HH2bo3dsiKA= +github.com/falcosecurity/testing v0.0.0-20230929141322-214d1fbb9327 h1:AF3VgWSrzKQoYDdXzxF3F8JmV6T3y1910gcA9c0ej4E= +github.com/falcosecurity/testing v0.0.0-20230929141322-214d1fbb9327/go.mod h1:yJVIonjfOTCI7yFQsJRU1PRFL57Ddad0HH2bo3dsiKA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= diff --git a/build/checker/main.go b/build/checker/main.go new file mode 100644 index 000000000..92c01ae72 --- /dev/null +++ b/build/checker/main.go @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2023 The Falco Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import "checker/cmd" + +func main() { + cmd.Execute() +} diff --git a/build/mitre_attack_checker/.python-version b/build/mitre_attack_checker/.python-version new file mode 100644 index 000000000..56d91d353 --- /dev/null +++ b/build/mitre_attack_checker/.python-version @@ -0,0 +1 @@ +3.10.12 diff --git a/build/mitre_attack_checker/README.md b/build/mitre_attack_checker/README.md new file mode 100644 index 000000000..4480339ba --- /dev/null +++ b/build/mitre_attack_checker/README.md @@ -0,0 +1,141 @@ +# Mitre ATT&CK Checker Module + +The Mitre ATT&CK Checker module aims to check the compliance of the Falco rules against the Mitre ATT&CK +framework. This module provides to Falco experts and Falco users a way to check default and custom +rules for Mitre ATT&CK extra tags. +This module uses STIX from the OASIS standards. Structured Threat Information Expression (STIX™) is a +language and serialization format used to exchange cyber threat intelligence (CTI) : + +- [STIX CTI documentation](https://oasis-open.github.io/cti-documentation/stix/intro) + +Leveraging STIX, Mitre ATT&CK Checker fetches the ATT&CK® STIX Data from MITRE ATT&CK repositories using +the `python-stix2` library implemented by OASIS: + +- [ATT&CK STIX Data repository](https://github.com/mitre-attack/attack-stix-data) +- [Python STIX2 repository](https://github.com/oasis-open/cti-python-stix2) + +The choice of a module is motivated by the packaging of a python code to integrate it into wider Falco +implementations. More precisely, the module can be used : + +- by the rules_overview_generator.py script +- by Falco users and experts to check their Falco rules files +- by other Falco components that need to check the validity of rules files + +## Build + +Requirements : + +- Python >= `3.10` +- Poetry >= `1.5.1` + +```sh +./build.sh +``` + +## Install + +Requirements : + +- Python >= `3.10` + +```sh +./install.sh +``` + +Or manualy using `pip` : + +```sh +pip install dist/falco_mitre_attack_checker-0.1.0-py3-none-any.whl +``` + +## Usage + +```sh +python -m falco_mitre_attack_checker --help +``` + +Using the stable falco rules : + +```sh +python -m falco_mitre_attack_checker -f ../../rules/falco_rules.yaml -o /tmp/ +``` + +## Development + +Requirements : + +- Python >= `3.10` +- Poetry >= `1.5.1` + +```sh +poetry check +poetry update +poetry install --sync +``` + +### Testing + +With coverage : + +```sh +poetry update +poetry run python -m pytest --cov=falco_mitre_attack_checker +``` + +``` +---------- coverage: platform linux, python 3.10.12-final-0 ---------- +Name Stmts Miss Cover +---------------------------------------------------------------------------- +falco_mitre_checker/__init__.py 0 0 100% +falco_mitre_checker/__main__.py 7 7 0% +falco_mitre_checker/api/__init__.py 0 0 100% +falco_mitre_checker/api/core.py 19 19 0% +falco_mitre_checker/cli/__init__.py 0 0 100% +falco_mitre_checker/cli/core.py 18 18 0% +falco_mitre_checker/engine/__init__.py 0 0 100% +falco_mitre_checker/engine/mitre_checker.py 46 1 98% +falco_mitre_checker/exceptions/__init__.py 0 0 100% +falco_mitre_checker/exceptions/rules_exceptions.py 8 0 100% +falco_mitre_checker/models/__init__.py 0 0 100% +falco_mitre_checker/models/falco_mitre_errors.py 16 0 100% +falco_mitre_checker/models/falco_mitre_relations.py 14 2 86% +falco_mitre_checker/parsers/__init__.py 0 0 100% +falco_mitre_checker/parsers/falco_rules.py 30 1 97% +falco_mitre_checker/parsers/mitre_stix.py 31 4 87% +falco_mitre_checker/tests/__init__.py 0 0 100% +falco_mitre_checker/tests/engine/__init__.py 0 0 100% +falco_mitre_checker/tests/engine/test_mitre_checker.py 41 0 100% +falco_mitre_checker/tests/parsers/__init__.py 0 0 100% +falco_mitre_checker/tests/parsers/test_falco_rules.py 18 0 100% +falco_mitre_checker/tests/parsers/test_mitre_stix.py 34 0 100% +falco_mitre_checker/tests/test_common.py 13 2 85% +falco_mitre_checker/utils/__init__.py 0 0 100% +falco_mitre_checker/utils/file.py 10 0 100% +falco_mitre_checker/utils/logger.py 36 7 81% +---------------------------------------------------------------------------- +TOTAL 341 61 82% +``` + +### Security + +You should run a vulnerability scanner every time you add a new dependency in projects : + +```sh +poetry update +poetry run python -m safety check +``` + +``` + Using non-commercial database + Found and scanned 33 packages + Timestamp 2023-10-02 13:43:51 + 0 vulnerabilities found + 0 vulnerabilities ignored ++=======================================================================================================+ + + No known security vulnerabilities found. + ++=======================================================================================================+ +``` + + diff --git a/build/mitre_attack_checker/build.sh b/build/mitre_attack_checker/build.sh new file mode 100755 index 000000000..2e10faf1c --- /dev/null +++ b/build/mitre_attack_checker/build.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +SCRIPT_PATH="$(realpath "$0")" +MODULE_DIR="$(dirname "${SCRIPT_PATH:?}")" + +# +# FUNCTIONS +# + +function check_requirement(){ + if ! eval "$@" >> /dev/null 2>&1 ; then + echo "! Fatal : missing requirement" + if [ -n "${*: -1}" ]; then echo "${@: -1}"; fi + exit 1 + fi +} + +# +# MAIN +# + +check_requirement poetry --version "Install poetry first" + +cd "${MODULE_DIR}" || exit +echo "Build environment :" +poetry env info + +echo "" +echo "Update dependencies" +poetry check +poetry update --without dev +poetry install --without dev --sync + +echo "Build Falco Mitre Checker module" +rm -rf "${MODULE_DIR}/dist" +poetry build --format wheel --no-cache + +echo "Built in dist/:" +ls "${MODULE_DIR}/dist/" diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/__init__.py b/build/mitre_attack_checker/falco_mitre_attack_checker/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/__main__.py b/build/mitre_attack_checker/falco_mitre_attack_checker/__main__.py new file mode 100644 index 000000000..2d31a09d8 --- /dev/null +++ b/build/mitre_attack_checker/falco_mitre_attack_checker/__main__.py @@ -0,0 +1,16 @@ +from falco_mitre_attack_checker.cli.core import cli +from falco_mitre_attack_checker.utils.logger import MitreCheckerLogger + + +def main(): + # init logger + MitreCheckerLogger() + # init cli + cli() + + +if __name__ == '__main__': + """ + for debug purpose + """ + main() diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/api/__init__.py b/build/mitre_attack_checker/falco_mitre_attack_checker/api/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/api/core.py b/build/mitre_attack_checker/falco_mitre_attack_checker/api/core.py new file mode 100644 index 000000000..3ccdbd32a --- /dev/null +++ b/build/mitre_attack_checker/falco_mitre_attack_checker/api/core.py @@ -0,0 +1,38 @@ +import logging +from pathlib import Path +from typing import List, Dict + +from falco_mitre_attack_checker.engine.mitre_checker import FalcoMitreChecker +from falco_mitre_attack_checker.models.falco_mitre_errors import FalcoMitreError +from falco_mitre_attack_checker.utils.logger import MitreCheckerLogger + +logger = logging.getLogger(MitreCheckerLogger.name) + + +def mitre_checker_engine(rules_files: List[Path], mitre_domain: str, mitre_version: str, + output_dir: Path = None) -> "Dict[str, List[FalcoMitreError]]": + """ + CLI core function to validate the rules against the Mitre ATT&CK's data. + :param rules_files: One or more falco rules files to check + :param mitre_domain: The name of the Mitre ATT&CK matrix domain to validate the rules. This name is + used to pull the data from the Mitre CTI's database. + :param mitre_version: The version of the Mitre ATT&CK to validate the rules. This version is used to + pull the data from the Mitre CTI's database. + :param output_dir: A folder path to dump the errors information in json format. + :param fix: If True, automatically generate the corrected falco rules file next to the original one + """ + mitre_checker = FalcoMitreChecker(mitre_domain, mitre_version) + errors_reports: Dict[str, List[FalcoMitreError]] = {} + for file in rules_files: + # validate the falco rules against the data of the mitre ATT&CK framework + errors = mitre_checker.validate(file) + errors_reports[file.stem] = errors + output_name = f"{file.stem}_mitre_errors.json" + output_path = output_dir / output_name if output_dir else file.parent / output_name + + FalcoMitreChecker.dump_errors(errors, output_path) + logger.info(f"Dumped errors report in '{output_path}'") + + logger.info(f"Found {len(errors)} Mitre errors") + + return errors_reports diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/cli/__init__.py b/build/mitre_attack_checker/falco_mitre_attack_checker/cli/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/cli/core.py b/build/mitre_attack_checker/falco_mitre_attack_checker/cli/core.py new file mode 100644 index 000000000..7c6909aa2 --- /dev/null +++ b/build/mitre_attack_checker/falco_mitre_attack_checker/cli/core.py @@ -0,0 +1,38 @@ +import logging +from pathlib import Path +from typing import List + +import typer + +from falco_mitre_attack_checker.api.core import mitre_checker_engine +from falco_mitre_attack_checker.exceptions.rules_exceptions import FalcoRulesFileContentError +from falco_mitre_attack_checker.utils.logger import MitreCheckerLogger + +app = typer.Typer(help=f"Mitre Checker", + no_args_is_help=True, + context_settings={"help_option_names": ["-h", "--help"]}) + +logger = logging.getLogger(MitreCheckerLogger.name) + + +@app.command() +def core(rules_files: List[Path] = typer.Option(..., "-f", "--file", + help="Path to a Falco rules file. " + "Repeat for multiple files validation."), + mitre_domain: str = typer.Option("enterprise-attack", "-d", "--domain", + help="Mitre ATT&CK domain name."), + mitre_version: str = typer.Option("13.1", "-V", "--Version", + help="Mitre ATT&CK domain version."), + output_dir: Path = typer.Option(None, "-o", "--output-dir", + help="Path to a directory to dump the error report for Mitre " + "ATT&CK.") + ): + try: + mitre_checker_engine(rules_files, mitre_domain, mitre_version, output_dir) + except FalcoRulesFileContentError as e: + logger.error(e.message) + typer.Exit(1) + + +def cli(): + app() diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/engine/__init__.py b/build/mitre_attack_checker/falco_mitre_attack_checker/engine/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/engine/mitre_checker.py b/build/mitre_attack_checker/falco_mitre_attack_checker/engine/mitre_checker.py new file mode 100644 index 000000000..82e4dc341 --- /dev/null +++ b/build/mitre_attack_checker/falco_mitre_attack_checker/engine/mitre_checker.py @@ -0,0 +1,111 @@ +import logging +from pathlib import Path +from typing import List + +from falco_mitre_attack_checker.models.falco_mitre_errors import \ + ErrorReason, FalcoMitreError, FalcoRulesErrors +from falco_mitre_attack_checker.models.falco_mitre_relations import MitreRelations +from falco_mitre_attack_checker.parsers.falco_rules import FalcoRulesParser +from falco_mitre_attack_checker.parsers.mitre_stix import MitreParser +from falco_mitre_attack_checker.utils.file import write_file +from falco_mitre_attack_checker.utils.logger import MitreCheckerLogger + +logger = logging.getLogger(MitreCheckerLogger.name) + + +class FalcoMitreChecker(object): + + def __init__(self, mitre_domain: str, mitre_domain_version: str): + logger.info(f"Load Mitre ATT&CK STIX Data for domain '{mitre_domain}' and version " + f"'{mitre_domain_version}'") + self.mitre_parser = MitreParser(mitre_domain, mitre_domain_version) + + def validate(self, falco_rules_file: Path) -> "List[FalcoMitreError]": + """ + This function validates the falco rules' extra tags against Mitre ATT&CK STIX Data when they + contain mitre information. + This method gets the mitre techniques or sub-techniques IDs and the mitre tactics (mitre phases) + names in the extra tags of each falco rules. + If the mitre techniques or sub-techniques IDs in the tags are not related to proper the mitre + tactics names by comparing them with the mitre data (STIX data from Mitre CTI), this method + considers that the rule contains an error. + For example, if the extra tags contain : + {"tags": ["T1611", "mitre_initial_access"] } + And the actual mitre domain is 'enterprise-attack' in version '13.1', the tags' rule will be + considered erroneous since the proper mitre phase for 'T1611' is 'privilege-escalation' in this + version. + :param falco_rules_file: A falco rule file to analyse against the Mitre ATT&CK STIX Data + :return: A list of models containing a description of each error in the falco rules for Mitre + ATT&CK + """ + logger.info(f"Audit Falco rules file '{falco_rules_file}' for Mitre ATT&CK") + falco_rules_parser = FalcoRulesParser(falco_rules_file) + falco_mitre_errors: List[FalcoMitreError] = [] + # build the model relation between technique (or sub-technique) ID and the mitre phase configured + # in each rule + rules_mitre_relations: MitreRelations = falco_rules_parser.get_mitre_relations() + for rule_name, rule_mitre_relation in rules_mitre_relations.rules.items(): + rule_tactics = rule_mitre_relation.tactics + all_mitre_tactics = [] + all_mitre_techniques_names = [] + all_mitre_techniques_urls = [] + + # verify each technique tag against mitre data + for rule_technique_or_tactic in rule_mitre_relation.techniques: + mitre_technique_or_tactic = self.mitre_parser.get_tactic_or_technique_by_id( + rule_technique_or_tactic) + mitre_tactics_names = self.mitre_parser.get_tactics_names(mitre_technique_or_tactic) + formatted_mitre_tactics_names = [f"mitre_{tactic.replace('-', '_')}" for tactic in + mitre_tactics_names] + # gather all correct mitre tactics & techniques of this rule + all_mitre_tactics += mitre_tactics_names + mitre_technique_name = self.mitre_parser.get_mitre_name(mitre_technique_or_tactic) + mitre_technique_url = self.mitre_parser.get_technique_external_reference( + mitre_technique_or_tactic)['url'] + all_mitre_techniques_names.append(mitre_technique_name) + all_mitre_techniques_urls.append(mitre_technique_url) + if not set(formatted_mitre_tactics_names).issubset(set(rule_tactics)): + # detect errors + # missing tactic tag in rule for this technique + falco_error = FalcoMitreError(rule=rule_name, + techniques_tags=[rule_technique_or_tactic], + tactics_tags=rule_tactics, + mitre_techniques_names=[mitre_technique_name], + mitre_tactics_names=mitre_tactics_names, + mitre_techniques_urls=[mitre_technique_url], + reasons=[ErrorReason.MISSING]) + + falco_mitre_errors.append(falco_error) + + # verify tactics + all_mitre_tactics_set = set(all_mitre_tactics) + if len(rule_tactics) > len(all_mitre_tactics_set): + # detect errors when too many tactic tags are included into the rule extra tags + falco_error = FalcoMitreError(rule=rule_name, + techniques_tags=rule_mitre_relation.techniques, + tactics_tags=rule_tactics, + mitre_techniques_names=list( + set(all_mitre_techniques_names)), + mitre_tactics_names=list(set(all_mitre_tactics_set)), + mitre_techniques_urls=list(set(all_mitre_techniques_urls)), + reasons=[ErrorReason.OVERDO]) + falco_mitre_errors.append(falco_error) + + return falco_mitre_errors + + def autofix(self, falco_rules_file: Path, falco_mitre_errors: List[FalcoMitreError]): + """ + Automatically fix Mitre tags in a falco rules file from a provided falco mitre errors report + :param falco_rules_file: the rules file to fix + :param falco_mitre_errors: the falco mitre error report for this file + """ + pass + + @staticmethod + def dump_errors(falco_mitre_errors: List[FalcoMitreError], output: Path) -> None: + """ + Write a list of falco mitre errors model to a file + :param output: output file to dump the errors + :param falco_mitre_errors: List of falco mitre errors models + """ + write_file(FalcoRulesErrors(errors=falco_mitre_errors).json(), output) diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/exceptions/__init__.py b/build/mitre_attack_checker/falco_mitre_attack_checker/exceptions/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/exceptions/rules_exceptions.py b/build/mitre_attack_checker/falco_mitre_attack_checker/exceptions/rules_exceptions.py new file mode 100644 index 000000000..409d96580 --- /dev/null +++ b/build/mitre_attack_checker/falco_mitre_attack_checker/exceptions/rules_exceptions.py @@ -0,0 +1,12 @@ +from pathlib import Path + + +class FalcoException(Exception): + pass + + +class FalcoRulesFileContentError(Exception): + def __init__(self, file: Path, message: str = "Wrong Falco Rules file content or format", *args): + self.file = file + self.message = message + super().__init__(self.message, args) diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/models/__init__.py b/build/mitre_attack_checker/falco_mitre_attack_checker/models/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/models/falco_mitre_errors.py b/build/mitre_attack_checker/falco_mitre_attack_checker/models/falco_mitre_errors.py new file mode 100644 index 000000000..0b666726a --- /dev/null +++ b/build/mitre_attack_checker/falco_mitre_attack_checker/models/falco_mitre_errors.py @@ -0,0 +1,42 @@ +from enum import Enum +from typing import List + +from pydantic import BaseModel + + +class ErrorReason(str, Enum): + MISSING = "One or more tactics tags are missing" + OVERDO = "Too many tactics tags" + + +class FalcoMitreError(BaseModel): + """ + This model describe an error located in a falco rule and related to its mitre tag + """ + # FALCO RELATED INFORMATION + # 'rule' is the rule description in the falco rules file + rule: str + # 'tactics_tags' are the tags of Mitre ATT&CK tactics in the current falco rule + tactics_tags: List[str] + # 'technique_tag' is the tag of a Mitre ATT&CK technique in the current falco rule + techniques_tags: List[str] + + # MITRE ATT&CK RELATED INFORMATION FROM STIX DATA + # 'mitre_tactics_names' are the Mitre ATT&CK's tactics name related to the technique tag in the + # current falco rule. These names are taken from STIX data. + mitre_tactics_names: List[str] + # 'mitre_technique_name' is the Mitre ATT&CK's technique name related to the technique tag in the + # current falco rule. This name is taken from STIX data. + mitre_techniques_names: List[str] + # 'mitre_technique_url' is the Mitre ATT&CK's technique url related to the technique tag in the + # current falco rule. This url is taken from STIX data. + mitre_techniques_urls: List[str] + # details about the error + reasons: List[ErrorReason] + + +class FalcoRulesErrors(BaseModel): + """ + This model is just useful to dump errors to disk + """ + errors: List[FalcoMitreError] diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/models/falco_mitre_relations.py b/build/mitre_attack_checker/falco_mitre_attack_checker/models/falco_mitre_relations.py new file mode 100644 index 000000000..10a0fb278 --- /dev/null +++ b/build/mitre_attack_checker/falco_mitre_attack_checker/models/falco_mitre_relations.py @@ -0,0 +1,29 @@ +from typing import List, Dict + +from pydantic import BaseModel + + +class MitreRelation(BaseModel): + """ + Simple relation between Mitre techniques or sub-techniques and the attached mitre phases + """ + techniques: List[str] + tactics: List[str] + + +class MitreRelations(BaseModel): + """ + This class builds a relation between a Falco rule and the extra tags it uses for Mitre ATT&CK + """ + rules: Dict[str, MitreRelation] = {} + + def __len__(self): + return len(self.rules) + + def add_techniques_and_tactics(self, rule_name: str, techniques_ids: List[str], + tactics_names: List[str]): + if rule_name in self.rules.keys(): + self.rules[rule_name].techniques += techniques_ids + self.rules[rule_name].tactics += tactics_names + else: + self.rules[rule_name] = MitreRelation(techniques=techniques_ids, tactics=tactics_names) diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/parsers/__init__.py b/build/mitre_attack_checker/falco_mitre_attack_checker/parsers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/parsers/falco_rules.py b/build/mitre_attack_checker/falco_mitre_attack_checker/parsers/falco_rules.py new file mode 100644 index 000000000..d64f07ffd --- /dev/null +++ b/build/mitre_attack_checker/falco_mitre_attack_checker/parsers/falco_rules.py @@ -0,0 +1,52 @@ +import re +from pathlib import Path +from typing import Dict + +from falco_mitre_attack_checker.exceptions.rules_exceptions import FalcoRulesFileContentError +from falco_mitre_attack_checker.models.falco_mitre_relations import MitreRelations +from falco_mitre_attack_checker.utils.file import read_yaml + + +class FalcoRulesParser(object): + """ + A Deserialization class for Falco rules file in order to define parsing methods + """ + VALIDATION_KEY = "required_engine_version" + rules: Dict + + def __init__(self, rules_file: Path): + self.path = rules_file + self.rules = read_yaml(rules_file) + self.validate() + + def validate(self): + """ + Simple function to check if the submitted file contains some requirements that Falco rules files + should have. + """ + error = FalcoRulesFileContentError(self.path, + message=f"Missing 'required_engine_version' conf in " + f"{self.path}, so wrong falco rules file format or " + f"not a rules file.") + try: + if not [items for items in self.rules if self.VALIDATION_KEY in items.keys()]: + raise error + except AttributeError: + raise error + + def get_mitre_relations(self) -> "MitreRelations": + """ + Build a relation model between techniques and mitre phases described in the falco rules + :return: the list of the relations + """ + # filter for rules with extra tags + filtered_rules = [rule for rule in self.rules if "tags" in rule.keys()] + relations = MitreRelations() + for rule in filtered_rules: + rule_desc: str = rule['rule'] + formatted_tags = [str(tag).upper() for tag in rule['tags']] + tactics = [tag.lower() for tag in formatted_tags if "MITRE_" in tag] + techniques = [tag for tag in formatted_tags if re.search("^TA?(\\d+).(\\d+)", tag)] + relations.add_techniques_and_tactics(rule_desc, techniques, tactics) + + return relations diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/parsers/mitre_stix.py b/build/mitre_attack_checker/falco_mitre_attack_checker/parsers/mitre_stix.py new file mode 100644 index 000000000..5f4b1b552 --- /dev/null +++ b/build/mitre_attack_checker/falco_mitre_attack_checker/parsers/mitre_stix.py @@ -0,0 +1,74 @@ +import logging +from typing import Dict, List + +import requests +from stix2 import MemoryStore, Filter, AttackPattern + +from falco_mitre_attack_checker.utils.logger import MitreCheckerLogger + +logger = logging.getLogger(MitreCheckerLogger.name) + + +class MitreParser(object): + """ + A Deserialization class for Mitre ATT&CK STIX2 data from Mitre CTI in order to define parsing methods + """ + # src is the source data directly fetched from STIX2 CTI bundle + src: MemoryStore + + def __init__(self, mitre_domain: str, mitre_domain_version: str): + """ + Init the Mitre parser by loading Mitre's STIX data from source. + https://github.com/mitre/cti/blob/master/USAGE.md + :param mitre_domain: either 'enterprise-attack', 'mobile-attack', or 'ics-attack' + :param mitre_domain_version: version of the mitre domain in format 'XX.XX' + """ + self.mitre_domain = mitre_domain + self.mitre_domain_version = mitre_domain_version + stix_json = requests.get( + f"https://raw.githubusercontent.com/mitre/cti/ATT%26CK-v{mitre_domain_version}/{mitre_domain}/{mitre_domain}.json").json() + self.src = MemoryStore(stix_data=stix_json["objects"]) + + def get_tactic_or_technique_by_id(self, external_id: str) -> "AttackPattern | None": + """ + Query Mitre CTI's STIX data to search a STIX technique definition by its ID + :param external_id: ID of the MITRE ATT&CK technique + :return: the technique definition in STIX2 data format + """ + # by default, a List is returned for STIX2 refs, but we expect only one technique per ID + try: + technique = self.src.query([ + Filter('external_references.external_id', '=', external_id), + Filter('type', 'in', ['x-mitre-tactic', 'attack-pattern']), + ])[0] + # Some techniques do not contain the 'x_mitre_deprecated' field + # So it is not exploitable with a filter, but we can do it by ourselves + if 'x_mitre_technique' in technique: + # return None if deprecated + return technique if not technique.x_mitre_deprecated else None + # considering technique is valid if no 'deprecation' field is defined + return technique + except IndexError: + logger.warning(f"Technique {external_id} doesn't exist for '{self.mitre_domain}' " + f"v{self.mitre_domain_version}") + return None + + @classmethod + def get_tactics_names(cls, ttp: AttackPattern) -> "List[str]": + """ + Get the mitre phase name (tactic) of a given technique or tactic. + If it is a tactic, only return the tactic name. + :param ttp: The MITRE ATT&CK data of a technique of a tactic + :return: The mitre phase names of the given technique or tactic + """ + return [tactic["phase_name"] for tactic in + ttp["kill_chain_phases"]] if "kill_chain_phases" in ttp else [ttp["x_mitre_shortname"]] + + @classmethod + def get_mitre_name(cls, ttp: AttackPattern) -> str: + return ttp['name'] + + @classmethod + def get_technique_external_reference(cls, ttp: AttackPattern) -> "Dict[str, str]": + return [reference for reference in ttp['external_references'] + if reference['source_name'] == "mitre-attack"][0] diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/tests/__init__.py b/build/mitre_attack_checker/falco_mitre_attack_checker/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/tests/engine/__init__.py b/build/mitre_attack_checker/falco_mitre_attack_checker/tests/engine/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/tests/engine/test_mitre_checker.py b/build/mitre_attack_checker/falco_mitre_attack_checker/tests/engine/test_mitre_checker.py new file mode 100644 index 000000000..adb2b54d0 --- /dev/null +++ b/build/mitre_attack_checker/falco_mitre_attack_checker/tests/engine/test_mitre_checker.py @@ -0,0 +1,63 @@ +from pathlib import Path +from typing import List + +from falco_mitre_attack_checker.engine.mitre_checker import FalcoMitreChecker +from falco_mitre_attack_checker.models.falco_mitre_errors import ErrorReason, FalcoRulesErrors, FalcoMitreError +from falco_mitre_attack_checker.tests.test_common import MITRE_DOMAIN, MITRE_VERSION, FALCO_RULES_FILE + +# global +mitre_checker = FalcoMitreChecker(MITRE_DOMAIN, MITRE_VERSION) +assert mitre_checker.mitre_parser + +errors: List[FalcoMitreError] = mitre_checker.validate(FALCO_RULES_FILE) +assert errors + + +def get_errors_by_rule(rule_name: str, + myerrors: "List[FalcoMitreError]") -> "List[FalcoMitreError]": + return [e for e in myerrors if e.rule == rule_name] + + +def get_error_by_technique(technique: str, + myerrors: "List[FalcoMitreError]") -> "FalcoMitreError": + return [e for e in myerrors if e.techniques_tags == [technique]][0] + + +def test_validate(): + # mitre tag not matching the technique phase + errors_1 = get_errors_by_rule('wrong mitre rule', errors) + assert errors_1 + assert len(errors_1) == 1 + error_1: FalcoMitreError = get_error_by_technique('T1610', errors_1) + assert error_1 + assert error_1.tactics_tags == ['mitre_lateral_movement'] + assert error_1.mitre_tactics_names == ['defense-evasion', 'execution'] + assert error_1.reasons == [ErrorReason.MISSING] + + # missing mitre tag for multiple techniques + # desc: one tactic tag is missing to fulfill all the mitre phases from the tagged techniques + errors_2 = get_errors_by_rule("wrong mitre rule multiple techniques and missing one tactic", errors) + assert len(errors_2) == 1 + error_1020: FalcoMitreError = get_error_by_technique('T1020', errors_2) + assert error_1020 + assert error_1020.tactics_tags == ['mitre_credential_access', 'mitre_discovery'] + assert error_1020.mitre_tactics_names == ['exfiltration'] + assert error_1020.reasons == [ErrorReason.MISSING] + + # too many tactics tags + errors_3 = get_errors_by_rule("too many tactics tags with multiple techniques", errors) + assert len(errors_3) == 1 + error_tactics: FalcoMitreError = errors_3[0] + assert error_tactics.tactics_tags.sort() == ["mitre_discovery", "mitre_exfiltration", + "mitre_credential_access", "mitre_execution"].sort() + assert error_tactics.mitre_tactics_names.sort() == ["mitre_discovery", "mitre_exfiltration", + "mitre_credential_access"].sort() + assert error_tactics.reasons == [ErrorReason.OVERDO] + + +def test_dump(): + output = Path('/tmp/test_falco_mitre_checker_dump.json') + mitre_checker.dump_errors(errors, output) + assert output.exists() + from_file = FalcoRulesErrors.parse_file(output) + assert from_file diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/tests/parsers/__init__.py b/build/mitre_attack_checker/falco_mitre_attack_checker/tests/parsers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/tests/parsers/test_falco_rules.py b/build/mitre_attack_checker/falco_mitre_attack_checker/tests/parsers/test_falco_rules.py new file mode 100644 index 000000000..3a42da4dd --- /dev/null +++ b/build/mitre_attack_checker/falco_mitre_attack_checker/tests/parsers/test_falco_rules.py @@ -0,0 +1,26 @@ +import pytest + +from falco_mitre_attack_checker.exceptions.rules_exceptions import FalcoRulesFileContentError +from falco_mitre_attack_checker.parsers.falco_rules import FalcoRulesParser +from falco_mitre_attack_checker.tests.test_common import NOT_FALCO_RULES_FILE, FALCO_RULES_FILE + +# test falco rules file validation +with pytest.raises(FalcoRulesFileContentError): + FalcoRulesParser(NOT_FALCO_RULES_FILE) + +falco_rules_parser = FalcoRulesParser(FALCO_RULES_FILE) +assert falco_rules_parser.rules + + +def test_get_mitre_relations(): + relations = falco_rules_parser.get_mitre_relations() + assert relations + assert len(relations) == 6 + + correct_mitre_rule = relations.rules['correct mitre rule'] + assert correct_mitre_rule.tactics == ['mitre_persistence'] + assert correct_mitre_rule.techniques == ['T1098'] + + wrong_mitre_rule = relations.rules['wrong mitre rule'] + assert wrong_mitre_rule.tactics == ['mitre_lateral_movement'] + assert wrong_mitre_rule.techniques == ['T1610'] diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/tests/parsers/test_mitre_stix.py b/build/mitre_attack_checker/falco_mitre_attack_checker/tests/parsers/test_mitre_stix.py new file mode 100644 index 000000000..d8d314fb4 --- /dev/null +++ b/build/mitre_attack_checker/falco_mitre_attack_checker/tests/parsers/test_mitre_stix.py @@ -0,0 +1,50 @@ +from falco_mitre_attack_checker.parsers.mitre_stix import MitreParser +from falco_mitre_attack_checker.tests.test_common import RESOURCES_DIR, MITRE_VERSION, MITRE_DOMAIN + +MITRE_STIX_DATAFILE = f"{RESOURCES_DIR}/mitre_cti_stix_13_1.json" + +mitre_parser = MitreParser(MITRE_DOMAIN, MITRE_VERSION) +assert mitre_parser.src + + +def test_get_tactic_or_technique_by_id(): + # technique + technique = mitre_parser.get_tactic_or_technique_by_id("T1548.001") + assert technique + assert not bool(technique.x_mitre_deprecated) + assert technique.type == 'attack-pattern' + assert technique.kill_chain_phases + kill_chain_names = [chain.kill_chain_name for chain in technique.kill_chain_phases] + assert 'mitre-attack' in kill_chain_names + + # tactic + tactic = mitre_parser.get_tactic_or_technique_by_id("TA0001") + assert tactic + assert tactic['type'] == "x-mitre-tactic" + + +def test_get_mitre_name(): + technique = mitre_parser.get_tactic_or_technique_by_id("T1548.001") + assert mitre_parser.get_mitre_name(technique) == "Setuid and Setgid" + + +def test_get_technique_external_reference(): + technique = mitre_parser.get_tactic_or_technique_by_id("T1548.001") + reference = mitre_parser.get_technique_external_reference(technique) + assert reference + assert reference['source_name'] == 'mitre-attack' + assert reference['url'] == "https://attack.mitre.org/techniques/T1548/001" + + +def test_get_tactics_names(): + # technique with multiple tactics + technique = mitre_parser.get_tactic_or_technique_by_id("T1610") + tactics_names = mitre_parser.get_tactics_names(technique) + assert tactics_names + assert tactics_names == ['defense-evasion', 'execution'] + + # tactic + tactic = mitre_parser.get_tactic_or_technique_by_id("TA0001") + tactics_names = mitre_parser.get_tactics_names(tactic) + assert tactics_names + assert tactics_names == ['initial-access'] diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/tests/resources/falco_rules_test.yaml b/build/mitre_attack_checker/falco_mitre_attack_checker/tests/resources/falco_rules_test.yaml new file mode 100644 index 000000000..91339f620 --- /dev/null +++ b/build/mitre_attack_checker/falco_mitre_attack_checker/tests/resources/falco_rules_test.yaml @@ -0,0 +1,88 @@ +- required_engine_version: 0.31.0 + +- macro: not a rule + condition: true + +- rule: not a mitre rule + desc: an attempt to write to any file below /etc + condition: write_etc_common + output: "File below /etc opened for writing (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid parent=%proc.pname pcmdline=%proc.pcmdline file=%fd.name program=%proc.name gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] container_id=%container.id image=%container.image.repository)" + priority: ERROR + tags: [ host, container, filesystem ] + +- rule: correct mitre rule + desc: an attempt to write to any file below /etc + condition: write_etc_common + output: "File below /etc opened for writing (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid parent=%proc.pname pcmdline=%proc.pcmdline file=%fd.name program=%proc.name gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] container_id=%container.id image=%container.image.repository)" + priority: ERROR + tags: [ host, container, network, mitre_persistence, T1098 ] + +- rule: wrong mitre rule + desc: > + Detect the initial process started by a container that is not in a list of allowed containers. + condition: container_started and container and not allowed_containers + output: Container started and not in allowed list (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid %container.info image=%container.image.repository:%container.image.tag) + priority: WARNING + tags: [ container, mitre_lateral_movement, T1610 ] + +- rule: correct mitre rule tactics tags with multiple techniques + desc: > + Directory traversal monitored file read - Web applications can be vulnerable to directory traversal attacks that allow accessing files outside of the web app's root directory (e.g. Arbitrary File Read bugs). + System directories like /etc are typically accessed via absolute paths. Access patterns outside of this (here path traversal) can be regarded as suspicious. + This rule includes failed file open attempts. + condition: (open_read or open_file_failed) and (etc_dir or user_ssh_directory or fd.name startswith /root/.ssh or fd.name contains "id_rsa") and directory_traversal and not proc.pname in (shell_binaries) + enabled: true + output: > + Read monitored file via directory traversal (username=%user.name useruid=%user.uid user_loginuid=%user.loginuid program=%proc.name exe=%proc.exepath + command=%proc.cmdline pid=%proc.pid parent=%proc.pname file=%fd.name fileraw=%fd.nameraw parent=%proc.pname + gparent=%proc.aname[2] container_id=%container.id image=%container.image.repository returncode=%evt.res cwd=%proc.cwd) + priority: WARNING + tags: [ host, container, filesystem, mitre_discovery, mitre_exfiltration, mitre_credential_access, T1555, T1212, T1020, T1552, T1083 ] + +- rule: too many tactics tags with multiple techniques + desc: > + Directory traversal monitored file read - Web applications can be vulnerable to directory traversal attacks that allow accessing files outside of the web app's root directory (e.g. Arbitrary File Read bugs). + System directories like /etc are typically accessed via absolute paths. Access patterns outside of this (here path traversal) can be regarded as suspicious. + This rule includes failed file open attempts. + condition: (open_read or open_file_failed) and (etc_dir or user_ssh_directory or fd.name startswith /root/.ssh or fd.name contains "id_rsa") and directory_traversal and not proc.pname in (shell_binaries) + enabled: true + output: > + Read monitored file via directory traversal (username=%user.name useruid=%user.uid user_loginuid=%user.loginuid program=%proc.name exe=%proc.exepath + command=%proc.cmdline pid=%proc.pid parent=%proc.pname file=%fd.name fileraw=%fd.nameraw parent=%proc.pname + gparent=%proc.aname[2] container_id=%container.id image=%container.image.repository returncode=%evt.res cwd=%proc.cwd) + priority: WARNING + tags: [ host, container, filesystem, mitre_discovery, mitre_exfiltration, mitre_credential_access, mitre_execution, T1555, T1212, T1020, T1552, T1083 ] + +- rule: wrong mitre rule multiple techniques and missing one tactic + desc: > + Read sensitive file untrusted - an attempt to read any sensitive file (e.g. files containing user/password/authentication + information). Exceptions are made for known trusted programs. + condition: > + sensitive_files and open_read + and proc_name_exists + and not proc.name in (user_mgmt_binaries, userexec_binaries, package_mgmt_binaries, + cron_binaries, read_sensitive_file_binaries, shell_binaries, hids_binaries, + vpn_binaries, mail_config_binaries, nomachine_binaries, sshkit_script_binaries, + in.proftpd, mandb, salt-call, salt-minion, postgres_mgmt_binaries, + google_oslogin_ + ) + and not cmp_cp_by_passwd + and not ansible_running_python + and not run_by_qualys + and not run_by_chef + and not run_by_google_accounts_daemon + and not user_read_sensitive_file_conditions + and not mandb_postinst + and not perl_running_plesk + and not perl_running_updmap + and not veritas_driver_script + and not perl_running_centrifydc + and not runuser_reading_pam + and not linux_bench_reading_etc_shadow + and not user_known_read_sensitive_files_activities + and not user_read_sensitive_file_containers + output: > + Sensitive file opened for reading by non-trusted program (user=%user.name user_loginuid=%user.loginuid program=%proc.name + command=%proc.cmdline pid=%proc.pid file=%fd.name parent=%proc.pname gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] container_id=%container.id image=%container.image.repository) + priority: WARNING + tags: [ host, container, filesystem, mitre_credential_access, mitre_discovery, T1555, T1212, T1020, T1552, T1083 ] diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/tests/resources/not_falco_rules_test.yaml b/build/mitre_attack_checker/falco_mitre_attack_checker/tests/resources/not_falco_rules_test.yaml new file mode 100644 index 000000000..d764d1272 --- /dev/null +++ b/build/mitre_attack_checker/falco_mitre_attack_checker/tests/resources/not_falco_rules_test.yaml @@ -0,0 +1,2 @@ +test: True +falco-rules: false \ No newline at end of file diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/tests/test_common.py b/build/mitre_attack_checker/falco_mitre_attack_checker/tests/test_common.py new file mode 100644 index 000000000..5dbc07f76 --- /dev/null +++ b/build/mitre_attack_checker/falco_mitre_attack_checker/tests/test_common.py @@ -0,0 +1,20 @@ +import os +from pathlib import Path + +from falco_mitre_attack_checker.utils.logger import MitreCheckerLogger + +MitreCheckerLogger() + +TEST_DIR = os.path.dirname(os.path.abspath(__file__)) +RESOURCES_DIR = f"{TEST_DIR}/resources" + +MITRE_VERSION = "13.1" +MITRE_DOMAIN = "enterprise-attack" + +FALCO_RULES_FILE = Path(f"{RESOURCES_DIR}/falco_rules_test.yaml") +NOT_FALCO_RULES_FILE = Path(f"{RESOURCES_DIR}/not_falco_rules_test.yaml") + + +def read_file(path: Path) -> "str": + with open(path, 'r') as f: + return str(f.read()) diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/utils/__init__.py b/build/mitre_attack_checker/falco_mitre_attack_checker/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/utils/file.py b/build/mitre_attack_checker/falco_mitre_attack_checker/utils/file.py new file mode 100644 index 000000000..40e177178 --- /dev/null +++ b/build/mitre_attack_checker/falco_mitre_attack_checker/utils/file.py @@ -0,0 +1,20 @@ +import os +from pathlib import Path +from typing import Dict + +import yaml + + +def read_yaml(path: Path) -> Dict: + """ + Validate format and read yaml file content + :param path: Path to a yaml file + :return: file content as dictionnary + """ + with open(path, "r") as p: + return yaml.safe_load(p.read()) + + +def write_file(content: str, output: Path): + with open(os.path.expandvars(output), 'w') as f: + f.write(content) diff --git a/build/mitre_attack_checker/falco_mitre_attack_checker/utils/logger.py b/build/mitre_attack_checker/falco_mitre_attack_checker/utils/logger.py new file mode 100644 index 000000000..6f5603c0c --- /dev/null +++ b/build/mitre_attack_checker/falco_mitre_attack_checker/utils/logger.py @@ -0,0 +1,55 @@ +import logging +import sys +from pathlib import Path +from typing import Optional + + +class LoggerFormatter(logging.Formatter): + green = "\x1b[92;21m" + cyan = "\x1b[96;21m" + yellow = "\x1b[93;21m" + red = "\x1b[91;21m" + reset = "\x1b[0m" + tag = "[%(levelname)s]" + message = " %(message)s" + file = " - (%(filename)s:%(lineno)d)" + + FORMATS = { + logging.DEBUG: cyan + tag + reset + message, + logging.INFO: green + tag + reset + message, + logging.WARNING: yellow + tag + reset + message + file, + logging.ERROR: red + tag + reset + message + file, + logging.FATAL: red + tag + reset + message + file + } + + def format(self, record): + log_fmt = self.FORMATS.get(record.levelno) + formatter = logging.Formatter(log_fmt) + return formatter.format(record) + + +class MitreCheckerLogger: + name: str = "mitre-checker" + formatter: logging.Formatter = LoggerFormatter() + + def __init__(self, debug: bool = False, logfile: Optional[Path] = None): + logger = logging.getLogger(self.name) + + # verbosity + level = logging.DEBUG if debug else logging.INFO + logger.setLevel(level) + + # add stdout logger to logging + stdout_handler = logging.StreamHandler(sys.stdout) + stdout_handler.setFormatter(self.formatter) + logger.addHandler(stdout_handler) + + # logfile output + if logfile is not None: + logfile_path = Path(logfile) + logfile_path.parent.mkdir(parents=True, exist_ok=True) + logfile_path.touch(exist_ok=True) + output_file_handler = logging.FileHandler(logfile) + output_file_handler.setLevel(level) + output_file_handler.setFormatter(self.formatter) + logger.addHandler(output_file_handler) diff --git a/build/mitre_attack_checker/install.sh b/build/mitre_attack_checker/install.sh new file mode 100755 index 000000000..4985cc6e2 --- /dev/null +++ b/build/mitre_attack_checker/install.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +SCRIPT_PATH="$(realpath "$0")" +MODULE_DIR="$(dirname "${SCRIPT_PATH:?}")" + +# +# FUNCTIONS +# + +function check_requirement(){ + if ! eval "$@" >> /dev/null 2>&1 ; then + echo "! Fatal : missing requirement" + if [ -n "${*: -1}" ]; then echo "${@: -1}"; fi + exit 1 + fi +} + +# +# MAIN +# + +cd "${MODULE_DIR}" || exit +pyversion=$(python --version) +echo "Install falco mitre checker module for: ${pyversion}" + +echo "" +wheel="$(find "./dist/" -type f -name "*.whl")" +python -m pip install "${wheel}" --force-reinstall --no-cache-dir + +echo "" +echo "OK" diff --git a/build/mitre_attack_checker/poetry.lock b/build/mitre_attack_checker/poetry.lock new file mode 100644 index 000000000..d0d686e1d --- /dev/null +++ b/build/mitre_attack_checker/poetry.lock @@ -0,0 +1,823 @@ +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. + +[[package]] +name = "antlr4-python3-runtime" +version = "4.9.3" +description = "ANTLR 4.9.3 runtime for Python 3.7" +optional = false +python-versions = "*" +files = [ + {file = "antlr4-python3-runtime-4.9.3.tar.gz", hash = "sha256:f224469b4168294902bb1efa80a8bf7855f24c99aef99cbefc1bcd3cce77881b"}, +] + +[[package]] +name = "certifi" +version = "2023.11.17" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, + {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.3.2" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf"}, + {file = "coverage-7.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda"}, + {file = "coverage-7.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a"}, + {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c"}, + {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f"}, + {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6"}, + {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148"}, + {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9"}, + {file = "coverage-7.3.2-cp310-cp310-win32.whl", hash = "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f"}, + {file = "coverage-7.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611"}, + {file = "coverage-7.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c"}, + {file = "coverage-7.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074"}, + {file = "coverage-7.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a"}, + {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3"}, + {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a"}, + {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1"}, + {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c"}, + {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312"}, + {file = "coverage-7.3.2-cp311-cp311-win32.whl", hash = "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640"}, + {file = "coverage-7.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2"}, + {file = "coverage-7.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836"}, + {file = "coverage-7.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63"}, + {file = "coverage-7.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216"}, + {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4"}, + {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf"}, + {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf"}, + {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84"}, + {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a"}, + {file = "coverage-7.3.2-cp312-cp312-win32.whl", hash = "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb"}, + {file = "coverage-7.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed"}, + {file = "coverage-7.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738"}, + {file = "coverage-7.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2"}, + {file = "coverage-7.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2"}, + {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c"}, + {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9"}, + {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82"}, + {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901"}, + {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76"}, + {file = "coverage-7.3.2-cp38-cp38-win32.whl", hash = "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92"}, + {file = "coverage-7.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a"}, + {file = "coverage-7.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce"}, + {file = "coverage-7.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9"}, + {file = "coverage-7.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f"}, + {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25"}, + {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9"}, + {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6"}, + {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc"}, + {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083"}, + {file = "coverage-7.3.2-cp39-cp39-win32.whl", hash = "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce"}, + {file = "coverage-7.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f"}, + {file = "coverage-7.3.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637"}, + {file = "coverage-7.3.2.tar.gz", hash = "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "dparse" +version = "0.6.3" +description = "A parser for Python dependency files" +optional = false +python-versions = ">=3.6" +files = [ + {file = "dparse-0.6.3-py3-none-any.whl", hash = "sha256:0d8fe18714056ca632d98b24fbfc4e9791d4e47065285ab486182288813a5318"}, + {file = "dparse-0.6.3.tar.gz", hash = "sha256:27bb8b4bcaefec3997697ba3f6e06b2447200ba273c0b085c3d012a04571b528"}, +] + +[package.dependencies] +packaging = "*" +tomli = {version = "*", markers = "python_version < \"3.11\""} + +[package.extras] +conda = ["pyyaml"] +pipenv = ["pipenv (<=2022.12.19)"] + +[[package]] +name = "exceptiongroup" +version = "1.2.0" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, + {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.6" +files = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "pluggy" +version = "1.3.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, + {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pydantic" +version = "1.10.13" +description = "Data validation and settings management using python type hints" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic-1.10.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:efff03cc7a4f29d9009d1c96ceb1e7a70a65cfe86e89d34e4a5f2ab1e5693737"}, + {file = "pydantic-1.10.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ecea2b9d80e5333303eeb77e180b90e95eea8f765d08c3d278cd56b00345d01"}, + {file = "pydantic-1.10.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1740068fd8e2ef6eb27a20e5651df000978edce6da6803c2bef0bc74540f9548"}, + {file = "pydantic-1.10.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84bafe2e60b5e78bc64a2941b4c071a4b7404c5c907f5f5a99b0139781e69ed8"}, + {file = "pydantic-1.10.13-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bc0898c12f8e9c97f6cd44c0ed70d55749eaf783716896960b4ecce2edfd2d69"}, + {file = "pydantic-1.10.13-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:654db58ae399fe6434e55325a2c3e959836bd17a6f6a0b6ca8107ea0571d2e17"}, + {file = "pydantic-1.10.13-cp310-cp310-win_amd64.whl", hash = "sha256:75ac15385a3534d887a99c713aa3da88a30fbd6204a5cd0dc4dab3d770b9bd2f"}, + {file = "pydantic-1.10.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c553f6a156deb868ba38a23cf0df886c63492e9257f60a79c0fd8e7173537653"}, + {file = "pydantic-1.10.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5e08865bc6464df8c7d61439ef4439829e3ab62ab1669cddea8dd00cd74b9ffe"}, + {file = "pydantic-1.10.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e31647d85a2013d926ce60b84f9dd5300d44535a9941fe825dc349ae1f760df9"}, + {file = "pydantic-1.10.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:210ce042e8f6f7c01168b2d84d4c9eb2b009fe7bf572c2266e235edf14bacd80"}, + {file = "pydantic-1.10.13-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8ae5dd6b721459bfa30805f4c25880e0dd78fc5b5879f9f7a692196ddcb5a580"}, + {file = "pydantic-1.10.13-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f8e81fc5fb17dae698f52bdd1c4f18b6ca674d7068242b2aff075f588301bbb0"}, + {file = "pydantic-1.10.13-cp311-cp311-win_amd64.whl", hash = "sha256:61d9dce220447fb74f45e73d7ff3b530e25db30192ad8d425166d43c5deb6df0"}, + {file = "pydantic-1.10.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4b03e42ec20286f052490423682016fd80fda830d8e4119f8ab13ec7464c0132"}, + {file = "pydantic-1.10.13-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f59ef915cac80275245824e9d771ee939133be38215555e9dc90c6cb148aaeb5"}, + {file = "pydantic-1.10.13-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a1f9f747851338933942db7af7b6ee8268568ef2ed86c4185c6ef4402e80ba8"}, + {file = "pydantic-1.10.13-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:97cce3ae7341f7620a0ba5ef6cf043975cd9d2b81f3aa5f4ea37928269bc1b87"}, + {file = "pydantic-1.10.13-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:854223752ba81e3abf663d685f105c64150873cc6f5d0c01d3e3220bcff7d36f"}, + {file = "pydantic-1.10.13-cp37-cp37m-win_amd64.whl", hash = "sha256:b97c1fac8c49be29486df85968682b0afa77e1b809aff74b83081cc115e52f33"}, + {file = "pydantic-1.10.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c958d053453a1c4b1c2062b05cd42d9d5c8eb67537b8d5a7e3c3032943ecd261"}, + {file = "pydantic-1.10.13-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4c5370a7edaac06daee3af1c8b1192e305bc102abcbf2a92374b5bc793818599"}, + {file = "pydantic-1.10.13-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d6f6e7305244bddb4414ba7094ce910560c907bdfa3501e9db1a7fd7eaea127"}, + {file = "pydantic-1.10.13-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3a3c792a58e1622667a2837512099eac62490cdfd63bd407993aaf200a4cf1f"}, + {file = "pydantic-1.10.13-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c636925f38b8db208e09d344c7aa4f29a86bb9947495dd6b6d376ad10334fb78"}, + {file = "pydantic-1.10.13-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:678bcf5591b63cc917100dc50ab6caebe597ac67e8c9ccb75e698f66038ea953"}, + {file = "pydantic-1.10.13-cp38-cp38-win_amd64.whl", hash = "sha256:6cf25c1a65c27923a17b3da28a0bdb99f62ee04230c931d83e888012851f4e7f"}, + {file = "pydantic-1.10.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8ef467901d7a41fa0ca6db9ae3ec0021e3f657ce2c208e98cd511f3161c762c6"}, + {file = "pydantic-1.10.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:968ac42970f57b8344ee08837b62f6ee6f53c33f603547a55571c954a4225691"}, + {file = "pydantic-1.10.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9849f031cf8a2f0a928fe885e5a04b08006d6d41876b8bbd2fc68a18f9f2e3fd"}, + {file = "pydantic-1.10.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56e3ff861c3b9c6857579de282ce8baabf443f42ffba355bf070770ed63e11e1"}, + {file = "pydantic-1.10.13-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f00790179497767aae6bcdc36355792c79e7bbb20b145ff449700eb076c5f96"}, + {file = "pydantic-1.10.13-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:75b297827b59bc229cac1a23a2f7a4ac0031068e5be0ce385be1462e7e17a35d"}, + {file = "pydantic-1.10.13-cp39-cp39-win_amd64.whl", hash = "sha256:e70ca129d2053fb8b728ee7d1af8e553a928d7e301a311094b8a0501adc8763d"}, + {file = "pydantic-1.10.13-py3-none-any.whl", hash = "sha256:b87326822e71bd5f313e7d3bfdc77ac3247035ac10b0c0618bd99dcf95b1e687"}, + {file = "pydantic-1.10.13.tar.gz", hash = "sha256:32c8b48dcd3b2ac4e78b0ba4af3a2c2eb6048cb75202f0ea7b34feb740efc340"}, +] + +[package.dependencies] +typing-extensions = ">=4.2.0" + +[package.extras] +dotenv = ["python-dotenv (>=0.10.4)"] +email = ["email-validator (>=1.0.3)"] + +[[package]] +name = "pyparsing" +version = "3.1.1" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb"}, + {file = "pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pytest" +version = "7.4.3" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"}, + {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "pytz" +version = "2023.3.post1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, + {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "ruamel-yaml" +version = "0.18.5" +description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruamel.yaml-0.18.5-py3-none-any.whl", hash = "sha256:a013ac02f99a69cdd6277d9664689eb1acba07069f912823177c5eced21a6ada"}, + {file = "ruamel.yaml-0.18.5.tar.gz", hash = "sha256:61917e3a35a569c1133a8f772e1226961bf5a1198bea7e23f06a0841dea1ab0e"}, +] + +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.13\""} + +[package.extras] +docs = ["mercurial (>5.7)", "ryd"] +jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] + +[[package]] +name = "ruamel-yaml-clib" +version = "0.2.8" +description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +optional = false +python-versions = ">=3.6" +files = [ + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win_amd64.whl", hash = "sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b"}, + {file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win32.whl", hash = "sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win32.whl", hash = "sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win32.whl", hash = "sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15"}, + {file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"}, +] + +[[package]] +name = "safety" +version = "2.3.5" +description = "Checks installed dependencies for known vulnerabilities and licenses." +optional = false +python-versions = "*" +files = [ + {file = "safety-2.3.5-py3-none-any.whl", hash = "sha256:2227fcac1b22b53c1615af78872b48348661691450aa25d6704a5504dbd1f7e2"}, + {file = "safety-2.3.5.tar.gz", hash = "sha256:a60c11f8952f412cbb165d70cb1f673a3b43a2ba9a93ce11f97e6a4de834aa3a"}, +] + +[package.dependencies] +Click = ">=8.0.2" +dparse = ">=0.6.2" +packaging = ">=21.0,<22.0" +requests = "*" +"ruamel.yaml" = ">=0.17.21" +setuptools = ">=19.3" + +[package.extras] +github = ["jinja2 (>=3.1.0)", "pygithub (>=1.43.3)"] +gitlab = ["python-gitlab (>=1.3.0)"] + +[[package]] +name = "setuptools" +version = "69.0.2" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-69.0.2-py3-none-any.whl", hash = "sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2"}, + {file = "setuptools-69.0.2.tar.gz", hash = "sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "simplejson" +version = "3.19.2" +description = "Simple, fast, extensible JSON encoder/decoder for Python" +optional = false +python-versions = ">=2.5, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "simplejson-3.19.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3471e95110dcaf901db16063b2e40fb394f8a9e99b3fe9ee3acc6f6ef72183a2"}, + {file = "simplejson-3.19.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:3194cd0d2c959062b94094c0a9f8780ffd38417a5322450a0db0ca1a23e7fbd2"}, + {file = "simplejson-3.19.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:8a390e56a7963e3946ff2049ee1eb218380e87c8a0e7608f7f8790ba19390867"}, + {file = "simplejson-3.19.2-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1537b3dd62d8aae644f3518c407aa8469e3fd0f179cdf86c5992792713ed717a"}, + {file = "simplejson-3.19.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a8617625369d2d03766413bff9e64310feafc9fc4f0ad2b902136f1a5cd8c6b0"}, + {file = "simplejson-3.19.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:2c433a412e96afb9a3ce36fa96c8e61a757af53e9c9192c97392f72871e18e69"}, + {file = "simplejson-3.19.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:f1c70249b15e4ce1a7d5340c97670a95f305ca79f376887759b43bb33288c973"}, + {file = "simplejson-3.19.2-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:287e39ba24e141b046812c880f4619d0ca9e617235d74abc27267194fc0c7835"}, + {file = "simplejson-3.19.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6f0a0b41dd05eefab547576bed0cf066595f3b20b083956b1405a6f17d1be6ad"}, + {file = "simplejson-3.19.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2f98d918f7f3aaf4b91f2b08c0c92b1774aea113334f7cde4fe40e777114dbe6"}, + {file = "simplejson-3.19.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d74beca677623481810c7052926365d5f07393c72cbf62d6cce29991b676402"}, + {file = "simplejson-3.19.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7f2398361508c560d0bf1773af19e9fe644e218f2a814a02210ac2c97ad70db0"}, + {file = "simplejson-3.19.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ad331349b0b9ca6da86064a3599c425c7a21cd41616e175ddba0866da32df48"}, + {file = "simplejson-3.19.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:332c848f02d71a649272b3f1feccacb7e4f7e6de4a2e6dc70a32645326f3d428"}, + {file = "simplejson-3.19.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25785d038281cd106c0d91a68b9930049b6464288cea59ba95b35ee37c2d23a5"}, + {file = "simplejson-3.19.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18955c1da6fc39d957adfa346f75226246b6569e096ac9e40f67d102278c3bcb"}, + {file = "simplejson-3.19.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:11cc3afd8160d44582543838b7e4f9aa5e97865322844b75d51bf4e0e413bb3e"}, + {file = "simplejson-3.19.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b01fda3e95d07a6148702a641e5e293b6da7863f8bc9b967f62db9461330562c"}, + {file = "simplejson-3.19.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:778331444917108fa8441f59af45886270d33ce8a23bfc4f9b192c0b2ecef1b3"}, + {file = "simplejson-3.19.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9eb117db8d7ed733a7317c4215c35993b815bf6aeab67523f1f11e108c040672"}, + {file = "simplejson-3.19.2-cp310-cp310-win32.whl", hash = "sha256:39b6d79f5cbfa3eb63a869639cfacf7c41d753c64f7801efc72692c1b2637ac7"}, + {file = "simplejson-3.19.2-cp310-cp310-win_amd64.whl", hash = "sha256:5675e9d8eeef0aa06093c1ff898413ade042d73dc920a03e8cea2fb68f62445a"}, + {file = "simplejson-3.19.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ed628c1431100b0b65387419551e822987396bee3c088a15d68446d92f554e0c"}, + {file = "simplejson-3.19.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:adcb3332979cbc941b8fff07181f06d2b608625edc0a4d8bc3ffc0be414ad0c4"}, + {file = "simplejson-3.19.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:08889f2f597ae965284d7b52a5c3928653a9406d88c93e3161180f0abc2433ba"}, + {file = "simplejson-3.19.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef7938a78447174e2616be223f496ddccdbf7854f7bf2ce716dbccd958cc7d13"}, + {file = "simplejson-3.19.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a970a2e6d5281d56cacf3dc82081c95c1f4da5a559e52469287457811db6a79b"}, + {file = "simplejson-3.19.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:554313db34d63eac3b3f42986aa9efddd1a481169c12b7be1e7512edebff8eaf"}, + {file = "simplejson-3.19.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d36081c0b1c12ea0ed62c202046dca11438bee48dd5240b7c8de8da62c620e9"}, + {file = "simplejson-3.19.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a3cd18e03b0ee54ea4319cdcce48357719ea487b53f92a469ba8ca8e39df285e"}, + {file = "simplejson-3.19.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:66e5dc13bfb17cd6ee764fc96ccafd6e405daa846a42baab81f4c60e15650414"}, + {file = "simplejson-3.19.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:972a7833d4a1fcf7a711c939e315721a88b988553fc770a5b6a5a64bd6ebeba3"}, + {file = "simplejson-3.19.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3e74355cb47e0cd399ead3477e29e2f50e1540952c22fb3504dda0184fc9819f"}, + {file = "simplejson-3.19.2-cp311-cp311-win32.whl", hash = "sha256:1dd4f692304854352c3e396e9b5f0a9c9e666868dd0bdc784e2ac4c93092d87b"}, + {file = "simplejson-3.19.2-cp311-cp311-win_amd64.whl", hash = "sha256:9300aee2a8b5992d0f4293d88deb59c218989833e3396c824b69ba330d04a589"}, + {file = "simplejson-3.19.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b8d940fd28eb34a7084877747a60873956893e377f15a32ad445fe66c972c3b8"}, + {file = "simplejson-3.19.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4969d974d9db826a2c07671273e6b27bc48e940738d768fa8f33b577f0978378"}, + {file = "simplejson-3.19.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c594642d6b13d225e10df5c16ee15b3398e21a35ecd6aee824f107a625690374"}, + {file = "simplejson-3.19.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2f5a398b5e77bb01b23d92872255e1bcb3c0c719a3be40b8df146570fe7781a"}, + {file = "simplejson-3.19.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:176a1b524a3bd3314ed47029a86d02d5a95cc0bee15bd3063a1e1ec62b947de6"}, + {file = "simplejson-3.19.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3c7363a8cb8c5238878ec96c5eb0fc5ca2cb11fc0c7d2379863d342c6ee367a"}, + {file = "simplejson-3.19.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:346820ae96aa90c7d52653539a57766f10f33dd4be609206c001432b59ddf89f"}, + {file = "simplejson-3.19.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de9a2792612ec6def556d1dc621fd6b2073aff015d64fba9f3e53349ad292734"}, + {file = "simplejson-3.19.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1c768e7584c45094dca4b334af361e43b0aaa4844c04945ac7d43379eeda9bc2"}, + {file = "simplejson-3.19.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:9652e59c022e62a5b58a6f9948b104e5bb96d3b06940c6482588176f40f4914b"}, + {file = "simplejson-3.19.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9c1a4393242e321e344213a90a1e3bf35d2f624aa8b8f6174d43e3c6b0e8f6eb"}, + {file = "simplejson-3.19.2-cp312-cp312-win32.whl", hash = "sha256:7cb98be113911cb0ad09e5523d0e2a926c09a465c9abb0784c9269efe4f95917"}, + {file = "simplejson-3.19.2-cp312-cp312-win_amd64.whl", hash = "sha256:6779105d2fcb7fcf794a6a2a233787f6bbd4731227333a072d8513b252ed374f"}, + {file = "simplejson-3.19.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:061e81ea2d62671fa9dea2c2bfbc1eec2617ae7651e366c7b4a2baf0a8c72cae"}, + {file = "simplejson-3.19.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4280e460e51f86ad76dc456acdbfa9513bdf329556ffc8c49e0200878ca57816"}, + {file = "simplejson-3.19.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11c39fbc4280d7420684494373b7c5904fa72a2b48ef543a56c2d412999c9e5d"}, + {file = "simplejson-3.19.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bccb3e88ec26ffa90f72229f983d3a5d1155e41a1171190fa723d4135523585b"}, + {file = "simplejson-3.19.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bb5b50dc6dd671eb46a605a3e2eb98deb4a9af787a08fcdddabe5d824bb9664"}, + {file = "simplejson-3.19.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:d94245caa3c61f760c4ce4953cfa76e7739b6f2cbfc94cc46fff6c050c2390c5"}, + {file = "simplejson-3.19.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d0e5ffc763678d48ecc8da836f2ae2dd1b6eb2d27a48671066f91694e575173c"}, + {file = "simplejson-3.19.2-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:d222a9ed082cd9f38b58923775152003765016342a12f08f8c123bf893461f28"}, + {file = "simplejson-3.19.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8434dcdd347459f9fd9c526117c01fe7ca7b016b6008dddc3c13471098f4f0dc"}, + {file = "simplejson-3.19.2-cp36-cp36m-win32.whl", hash = "sha256:c9ac1c2678abf9270e7228133e5b77c6c3c930ad33a3c1dfbdd76ff2c33b7b50"}, + {file = "simplejson-3.19.2-cp36-cp36m-win_amd64.whl", hash = "sha256:92c4a4a2b1f4846cd4364855cbac83efc48ff5a7d7c06ba014c792dd96483f6f"}, + {file = "simplejson-3.19.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0d551dc931638e2102b8549836a1632e6e7cf620af3d093a7456aa642bff601d"}, + {file = "simplejson-3.19.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73a8a4653f2e809049999d63530180d7b5a344b23a793502413ad1ecea9a0290"}, + {file = "simplejson-3.19.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:40847f617287a38623507d08cbcb75d51cf9d4f9551dd6321df40215128325a3"}, + {file = "simplejson-3.19.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be893258d5b68dd3a8cba8deb35dc6411db844a9d35268a8d3793b9d9a256f80"}, + {file = "simplejson-3.19.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9eb3cff1b7d71aa50c89a0536f469cb8d6dcdd585d8f14fb8500d822f3bdee4"}, + {file = "simplejson-3.19.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d0f402e787e6e7ee7876c8b05e2fe6464820d9f35ba3f172e95b5f8b699f6c7f"}, + {file = "simplejson-3.19.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:fbbcc6b0639aa09b9649f36f1bcb347b19403fe44109948392fbb5ea69e48c3e"}, + {file = "simplejson-3.19.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:2fc697be37585eded0c8581c4788fcfac0e3f84ca635b73a5bf360e28c8ea1a2"}, + {file = "simplejson-3.19.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b0a3eb6dd39cce23801a50c01a0976971498da49bc8a0590ce311492b82c44b"}, + {file = "simplejson-3.19.2-cp37-cp37m-win32.whl", hash = "sha256:49f9da0d6cd17b600a178439d7d2d57c5ef01f816b1e0e875e8e8b3b42db2693"}, + {file = "simplejson-3.19.2-cp37-cp37m-win_amd64.whl", hash = "sha256:c87c22bd6a987aca976e3d3e23806d17f65426191db36d40da4ae16a6a494cbc"}, + {file = "simplejson-3.19.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9e4c166f743bb42c5fcc60760fb1c3623e8fda94f6619534217b083e08644b46"}, + {file = "simplejson-3.19.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0a48679310e1dd5c9f03481799311a65d343748fe86850b7fb41df4e2c00c087"}, + {file = "simplejson-3.19.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0521e0f07cb56415fdb3aae0bbd8701eb31a9dfef47bb57206075a0584ab2a2"}, + {file = "simplejson-3.19.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d2d5119b1d7a1ed286b8af37357116072fc96700bce3bec5bb81b2e7057ab41"}, + {file = "simplejson-3.19.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c1467d939932901a97ba4f979e8f2642415fcf02ea12f53a4e3206c9c03bc17"}, + {file = "simplejson-3.19.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49aaf4546f6023c44d7e7136be84a03a4237f0b2b5fb2b17c3e3770a758fc1a0"}, + {file = "simplejson-3.19.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60848ab779195b72382841fc3fa4f71698a98d9589b0a081a9399904487b5832"}, + {file = "simplejson-3.19.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0436a70d8eb42bea4fe1a1c32d371d9bb3b62c637969cb33970ad624d5a3336a"}, + {file = "simplejson-3.19.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:49e0e3faf3070abdf71a5c80a97c1afc059b4f45a5aa62de0c2ca0444b51669b"}, + {file = "simplejson-3.19.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ff836cd4041e16003549449cc0a5e372f6b6f871eb89007ab0ee18fb2800fded"}, + {file = "simplejson-3.19.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3848427b65e31bea2c11f521b6fc7a3145d6e501a1038529da2391aff5970f2f"}, + {file = "simplejson-3.19.2-cp38-cp38-win32.whl", hash = "sha256:3f39bb1f6e620f3e158c8b2eaf1b3e3e54408baca96a02fe891794705e788637"}, + {file = "simplejson-3.19.2-cp38-cp38-win_amd64.whl", hash = "sha256:0405984f3ec1d3f8777c4adc33eac7ab7a3e629f3b1c05fdded63acc7cf01137"}, + {file = "simplejson-3.19.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:445a96543948c011a3a47c8e0f9d61e9785df2544ea5be5ab3bc2be4bd8a2565"}, + {file = "simplejson-3.19.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4a8c3cc4f9dfc33220246760358c8265dad6e1104f25f0077bbca692d616d358"}, + {file = "simplejson-3.19.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af9c7e6669c4d0ad7362f79cb2ab6784d71147503e62b57e3d95c4a0f222c01c"}, + {file = "simplejson-3.19.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:064300a4ea17d1cd9ea1706aa0590dcb3be81112aac30233823ee494f02cb78a"}, + {file = "simplejson-3.19.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9453419ea2ab9b21d925d0fd7e3a132a178a191881fab4169b6f96e118cc25bb"}, + {file = "simplejson-3.19.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e038c615b3906df4c3be8db16b3e24821d26c55177638ea47b3f8f73615111c"}, + {file = "simplejson-3.19.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16ca9c90da4b1f50f089e14485db8c20cbfff2d55424062791a7392b5a9b3ff9"}, + {file = "simplejson-3.19.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1018bd0d70ce85f165185d2227c71e3b1e446186f9fa9f971b69eee223e1e3cd"}, + {file = "simplejson-3.19.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e8dd53a8706b15bc0e34f00e6150fbefb35d2fd9235d095b4f83b3c5ed4fa11d"}, + {file = "simplejson-3.19.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:2d022b14d7758bfb98405672953fe5c202ea8a9ccf9f6713c5bd0718eba286fd"}, + {file = "simplejson-3.19.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:febffa5b1eda6622d44b245b0685aff6fb555ce0ed734e2d7b1c3acd018a2cff"}, + {file = "simplejson-3.19.2-cp39-cp39-win32.whl", hash = "sha256:4edcd0bf70087b244ba77038db23cd98a1ace2f91b4a3ecef22036314d77ac23"}, + {file = "simplejson-3.19.2-cp39-cp39-win_amd64.whl", hash = "sha256:aad7405c033d32c751d98d3a65801e2797ae77fac284a539f6c3a3e13005edc4"}, + {file = "simplejson-3.19.2-py3-none-any.whl", hash = "sha256:bcedf4cae0d47839fee7de344f96b5694ca53c786f28b5f773d4f0b265a159eb"}, + {file = "simplejson-3.19.2.tar.gz", hash = "sha256:9eb442a2442ce417801c912df68e1f6ccfcd41577ae7274953ab3ad24ef7d82c"}, +] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "stix2" +version = "3.0.1" +description = "Produce and consume STIX 2 JSON content" +optional = false +python-versions = ">=3.6" +files = [ + {file = "stix2-3.0.1-py2.py3-none-any.whl", hash = "sha256:827acf0b5b319c1b857c9db0d54907bb438b2b32312d236c891a305ad49b0ba2"}, + {file = "stix2-3.0.1.tar.gz", hash = "sha256:2a2718dc3451c84c709990b2ca220cc39c75ed23e0864d7e8d8190a9365b0cbf"}, +] + +[package.dependencies] +pytz = "*" +requests = "*" +simplejson = "*" +stix2-patterns = ">=1.2.0" + +[package.extras] +semantic = ["haversine", "rapidfuzz"] +taxii = ["taxii2-client (>=2.3.0)"] + +[[package]] +name = "stix2-patterns" +version = "2.0.0" +description = "Validate STIX 2 Patterns." +optional = false +python-versions = ">=3.6" +files = [ + {file = "stix2-patterns-2.0.0.tar.gz", hash = "sha256:07750c5a5af2c758e9d2aa4dde9d8e04bcd162ac2a9b0b4c4de4481d443efa08"}, + {file = "stix2_patterns-2.0.0-py2.py3-none-any.whl", hash = "sha256:ca4d68b2db42ed99794a418388769d2676ca828e9cac0b8629e73cd3f68f6458"}, +] + +[package.dependencies] +antlr4-python3-runtime = ">=4.9.0,<4.10.0" +six = "*" + +[package.extras] +dev = ["bumpversion", "check-manifest", "coverage", "pre-commit", "pytest", "pytest-cov", "sphinx", "sphinx-prompt", "tox"] +docs = ["sphinx", "sphinx-prompt"] +test = ["coverage", "pytest", "pytest-cov"] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "typer" +version = "0.9.0" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.6" +files = [ + {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, + {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, +] + +[package.dependencies] +click = ">=7.1.1,<9.0.0" +typing-extensions = ">=3.7.4.3" + +[package.extras] +all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] +dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] +doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] +test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] + +[[package]] +name = "typing-extensions" +version = "4.8.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, + {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, +] + +[[package]] +name = "urllib3" +version = "2.1.0" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, + {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.10" +content-hash = "adbf41d469cd7b6a5e736194fa484063e644a43ffb2426173931336e3ae0bf64" diff --git a/build/mitre_attack_checker/pyproject.toml b/build/mitre_attack_checker/pyproject.toml new file mode 100644 index 000000000..89f2476cd --- /dev/null +++ b/build/mitre_attack_checker/pyproject.toml @@ -0,0 +1,27 @@ +[tool.poetry] +name = "falco_mitre_attack_checker" +version = "0.1.0" +description = "Audit module to validate Falco rules against the Mitre ATT&CK Framework" +authors = ["The Falco Authors "] +license = "Apache-2.0" +readme = "README.md" +packages = [{include = "falco_mitre_attack_checker"}] + +[tool.poetry.dependencies] +python = "^3.10" +pydantic = "^1.10.9" +stix2 = "^3.0.1" +typer = "^0.9.0" +pyyaml = "^6.0" + +[tool.poetry.group.dev.dependencies] +pytest-cov = "^4.1.0" +pytest = "^7.4.0" +safety = "^2.3.5" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry.scripts] +falco_mitre_attack_checker = "mitre_attack_checker.__main__:main" diff --git a/build/registry/cmd.go b/build/registry/cmd.go index b389a439b..860ab8319 100644 --- a/build/registry/cmd.go +++ b/build/registry/cmd.go @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2023 The Falco Authors. @@ -86,7 +87,7 @@ func doUploadToS3(registryFilename, gitTag string) error { rulesfileInfo := reg.RulesfileByName(pt.Name) if rulesfileInfo == nil { - return fmt.Errorf("could not find rulesfile %s in registry", pt.Name) + return fmt.Errorf("could not find rulesfile %s in the registry (reserved or archived are ignored)", pt.Name) } tmpDir, err := os.MkdirTemp("", "falco-artifacts-to-upload") @@ -109,29 +110,29 @@ func doUploadToS3(registryFilename, gitTag string) error { return nil } -func doPushToOCI(registryFilename, gitTag string) error { +func doPushToOCI(registryFilename, gitTag string) (*string, error) { var ociRepoPrefix, repoGit, user, token string var found bool if token, found = os.LookupEnv(RegistryTokenEnv); !found { - return fmt.Errorf("environment variable with key %q not found, please set it before running this tool", RegistryTokenEnv) + return nil, fmt.Errorf("environment variable with key %q not found, please set it before running this tool", RegistryTokenEnv) } if user, found = os.LookupEnv(RegistryUserEnv); !found { - return fmt.Errorf("environment variable with key %q not found, please set it before running this tool", RegistryUserEnv) + return nil, fmt.Errorf("environment variable with key %q not found, please set it before running this tool", RegistryUserEnv) } if ociRepoPrefix, found = os.LookupEnv(OCIRepoPrefixEnv); !found { - return fmt.Errorf("environment variable with key %q not found, please set it before running this tool", OCIRepoPrefixEnv) + return nil, fmt.Errorf("environment variable with key %q not found, please set it before running this tool", OCIRepoPrefixEnv) } if repoGit, found = os.LookupEnv(RepoGithubEnv); !found { - return fmt.Errorf("environment variable with key %q not found, please set it before running this tool", RepoGithubEnv) + return nil, fmt.Errorf("environment variable with key %q not found, please set it before running this tool", RepoGithubEnv) } pt, err := parseGitTag(gitTag) if err != nil { - return err + return nil, err } cred := &auth.Credential{ @@ -144,18 +145,18 @@ func doPushToOCI(registryFilename, gitTag string) error { reg, err := loadRegistryFromFile(registryFilename) if err != nil { - return fmt.Errorf("could not read registry from %s: %w", registryFilename, err) + return nil, fmt.Errorf("could not read registry from %s: %w", registryFilename, err) } rulesfileInfo := reg.RulesfileByName(pt.Name) if rulesfileInfo == nil { - return fmt.Errorf("could not find rulesfile %s in registry", pt.Name) + return nil, fmt.Errorf("could not find rulesfile %s in the registry (reserved or archived are ignored)", pt.Name) } // Create the repository object for the ref. var repo *repository.Repository if repo, err = repository.NewRepository(ociRepoRef, repository.WithClient(client)); err != nil { - return fmt.Errorf("unable to create repository for ref %q: %w", ociRepoRef, err) + return nil, fmt.Errorf("unable to create repository for ref %q: %w", ociRepoRef, err) } existingTags, _ := repo.Tags(context.Background()) @@ -171,25 +172,45 @@ func doPushToOCI(registryFilename, gitTag string) error { tgzFile := filepath.Join(tmpDir, filepath.Base(rulesfileInfo.Path)+".tar.gz") if err = tarGzSingleFile(tgzFile, rulesfileInfo.Path); err != nil { - return fmt.Errorf("could not compress %s: %w", rulesfileInfo.Path, err) + return nil, fmt.Errorf("could not compress %s: %w", rulesfileInfo.Path, err) } defer os.RemoveAll(tgzFile) config, err := rulesfileConfig(rulesfileInfo.Name, pt.Version(), rulesfileInfo.Path) if err != nil { - return fmt.Errorf("could not generate configuration layer for rulesfiles %q: %w", rulesfileInfo.Path, err) + return nil, fmt.Errorf("could not generate configuration layer for rulesfiles %q: %w", rulesfileInfo.Path, err) } - if err = pushCompressedRulesfile(client, tgzFile, ociRepoRef, repoGit, tagsToUpdate, config); err != nil { - return fmt.Errorf("could not push %s to %s with source %s and tags %v: %w", tgzFile, ociRepoRef, repoGit, tagsToUpdate, err) + + digest, err := pushCompressedRulesfile(client, tgzFile, ociRepoRef, repoGit, tagsToUpdate, config) + if err != nil { + return nil, fmt.Errorf("could not push %s to %s with source %s and tags %v: %w", tgzFile, ociRepoRef, repoGit, tagsToUpdate, err) } - return nil + // ociRepoDigest is a string that looks like ghcr.io/falcosecurity/rules/falco-rules@sha256:123456... + ociRepoDigest := fmt.Sprintf("%s@%s", ociRepoRef, *digest) + + return &ociRepoDigest, nil } func rulesOciRepos(registryEntries *Registry, ociRepoPrefix string) (map[string]string, error) { + var user, token string var repo *repository.Repository var err error - ociClient := authn.NewClient(authn.WithCredentials(&auth.EmptyCredential)) + var foundUser, foundToken bool + var cred *auth.Credential + + user, foundUser = os.LookupEnv(RegistryUserEnv) + token, foundToken = os.LookupEnv(RegistryTokenEnv) + + if !foundUser && !foundToken { + cred = &auth.EmptyCredential + } else { + cred = &auth.Credential{ + Username: user, + Password: token, + } + } + ociClient := authn.NewClient(authn.WithCredentials(cred)) ociEntries := make(map[string]string) for _, entry := range registryEntries.Rulesfiles { @@ -249,7 +270,7 @@ func main() { updateIndexCmd := &cobra.Command{ Use: "update-index ", - Short: "Update an index file for artifacts distribution using registry data", + Short: "Update an index file for artifacts distribution using registry data, authenticates to the registry if REGISTRY_USER, REGISTRY_TOKEN are set", Args: cobra.ExactArgs(2), DisableFlagsInUseLine: true, RunE: func(c *cobra.Command, args []string) error { @@ -263,7 +284,13 @@ func main() { Args: cobra.ExactArgs(2), DisableFlagsInUseLine: true, RunE: func(c *cobra.Command, args []string) error { - return doPushToOCI(args[0], args[1]) + ociRepoDigest, err := doPushToOCI(args[0], args[1]) + if err != nil { + return err + } + fmt.Println(*ociRepoDigest) + + return nil }, } diff --git a/build/registry/config_layer.go b/build/registry/config_layer.go index 69b7de81b..68e482c59 100644 --- a/build/registry/config_layer.go +++ b/build/registry/config_layer.go @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2023 The Falco Authors. diff --git a/build/registry/go.mod b/build/registry/go.mod index 9a3e7bf81..efa46b4e1 100644 --- a/build/registry/go.mod +++ b/build/registry/go.mod @@ -1,103 +1,71 @@ module registry -go 1.19 +go 1.21 + +toolchain go1.21.0 require ( - github.com/aws/aws-sdk-go v1.44.183 + github.com/aws/aws-sdk-go v1.44.288 github.com/blang/semver v3.5.1+incompatible - github.com/falcosecurity/falcoctl v0.3.0-rc5 - github.com/spf13/cobra v1.6.1 - github.com/stretchr/testify v1.7.2 + github.com/falcosecurity/falcoctl v0.6.1 + github.com/spf13/cobra v1.7.0 + github.com/stretchr/testify v1.8.4 gopkg.in/yaml.v2 v2.4.0 - k8s.io/klog/v2 v2.80.1 - oras.land/oras-go/v2 v2.0.0-rc.3 + k8s.io/klog/v2 v2.100.1 + oras.land/oras-go/v2 v2.2.1 ) require ( - atomicgo.dev/cursor v0.1.1 // indirect - atomicgo.dev/keyboard v0.2.8 // indirect - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect - github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect + atomicgo.dev/cursor v0.2.0 // indirect + atomicgo.dev/keyboard v0.2.9 // indirect + atomicgo.dev/schedule v0.1.0 // indirect + cloud.google.com/go/compute v1.23.0 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect github.com/containerd/console v1.0.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/cli v20.10.17+incompatible // indirect - github.com/docker/docker v20.10.17+incompatible // indirect - github.com/docker/docker-credential-helpers v0.6.4 // indirect - github.com/emicklei/go-restful v2.9.5+incompatible // indirect - github.com/evanphx/json-patch v4.12.0+incompatible // indirect - github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect - github.com/go-errors/errors v1.0.1 // indirect - github.com/go-logr/logr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.5 // indirect - github.com/go-openapi/swag v0.19.14 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/google/btree v1.0.1 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/gofuzz v1.2.0 // indirect - github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.2.0 // indirect - github.com/gookit/color v1.5.0 // indirect - github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect - github.com/imdario/mergo v0.3.12 // indirect - github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/docker/cli v24.0.5+incompatible // indirect + github.com/docker/docker v24.0.5+incompatible // indirect + github.com/docker/docker-credential-helpers v0.8.0 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/gookit/color v1.5.4 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect - github.com/lithammer/fuzzysearch v1.1.5 // indirect - github.com/mailru/easyjson v0.7.6 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect - github.com/mattn/go-runewidth v0.0.13 // indirect - github.com/mitchellh/go-wordwrap v1.0.0 // indirect - github.com/moby/spdystream v0.2.0 // indirect - github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/opencontainers/distribution-spec/specs-go v0.0.0-20220620172159-4ab4752c3b86 // indirect + github.com/lithammer/fuzzysearch v1.1.8 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect - github.com/oras-project/artifacts-spec v1.0.0-rc.2 // indirect - github.com/peterbourgon/diskv v2.0.1+incompatible // indirect + github.com/opencontainers/image-spec v1.1.0-rc4 // indirect + github.com/oras-project/oras-credentials-go v0.3.0 // indirect + github.com/pelletier/go-toml/v2 v2.0.9 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/pterm/pterm v0.12.45 // indirect - github.com/rivo/uniseg v0.2.0 // indirect - github.com/russross/blackfriday v1.5.2 // indirect - github.com/sirupsen/logrus v1.9.0 // indirect + github.com/pterm/pterm v0.12.67 // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/spf13/afero v1.9.5 // indirect + github.com/spf13/cast v1.5.1 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect - github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect - go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect - golang.org/x/net v0.2.0 // indirect - golang.org/x/oauth2 v0.2.0 // indirect - golang.org/x/sync v0.0.0-20220907140024-f12130a52804 // indirect - golang.org/x/sys v0.2.0 // indirect - golang.org/x/term v0.2.0 // indirect - golang.org/x/text v0.4.0 // indirect - golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect + github.com/spf13/viper v1.16.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect + golang.org/x/net v0.14.0 // indirect + golang.org/x/oauth2 v0.11.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.11.0 // indirect + golang.org/x/term v0.11.0 // indirect + golang.org/x/text v0.12.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.28.0 // indirect - gopkg.in/inf.v0 v0.9.1 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - gotest.tools/v3 v3.4.0 // indirect - k8s.io/api v0.24.3 // indirect - k8s.io/apimachinery v0.24.3 // indirect - k8s.io/cli-runtime v0.24.3 // indirect - k8s.io/client-go v0.24.3 // indirect - k8s.io/component-base v0.24.3 // indirect - k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect - k8s.io/kubectl v0.24.3 // indirect - k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect - sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect - sigs.k8s.io/kustomize/api v0.11.4 // indirect - sigs.k8s.io/kustomize/kyaml v0.13.6 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect - sigs.k8s.io/yaml v1.2.0 // indirect + gotest.tools/v3 v3.5.0 // indirect ) diff --git a/build/registry/go.sum b/build/registry/go.sum index 4493cd0aa..4e91ae6f2 100644 --- a/build/registry/go.sum +++ b/build/registry/go.sum @@ -1,12 +1,17 @@ -atomicgo.dev/cursor v0.1.1 h1:0t9sxQomCTRh5ug+hAMCs59x/UmC9QL6Ci5uosINKD4= -atomicgo.dev/cursor v0.1.1/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU= -atomicgo.dev/keyboard v0.2.8 h1:Di09BitwZgdTV1hPyX/b9Cqxi8HVuJQwWivnZUEqlj4= -atomicgo.dev/keyboard v0.2.8/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= +atomicgo.dev/assert v0.0.2 h1:FiKeMiZSgRrZsPo9qn/7vmr7mCsh5SZyXY4YGYiYwrg= +atomicgo.dev/assert v0.0.2/go.mod h1:ut4NcI3QDdJtlmAxQULOmA13Gz6e2DWbSAS8RUOmNYQ= +atomicgo.dev/cursor v0.2.0 h1:H6XN5alUJ52FZZUkI7AlJbUc1aW38GWZalpYRPpoPOw= +atomicgo.dev/cursor v0.2.0/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU= +atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8= +atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= +atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs= +atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -19,18 +24,19 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= +cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -40,72 +46,39 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU= -github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8= github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII= github.com/MarvinJWendt/testza v0.2.10/go.mod h1:pd+VWsoGUiFtq+hRKSU1Bktnn+DMCSrDrXDpX2bG66k= github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzXjB69adAhzZkI= github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/2oUqKc6bF2c= -github.com/MarvinJWendt/testza v0.4.2 h1:Vbw9GkSB5erJI2BPnBL9SVGV9myE+XmUSFahBGUhW2Q= github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi+zB4= +github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY= +github.com/Shopify/logrus-bugsnag v0.0.0-20230117174420-439a4b8ba167 h1:4sc2y3LzYFk3Na8xMtfnJ5T1N5kA+MsU8dTJN6IjJqk= +github.com/Shopify/logrus-bugsnag v0.0.0-20230117174420-439a4b8ba167/go.mod h1:nBISMsZeFRL0qdZ1pKCSFv0j4veW0nOdVpOa49IMLeI= github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= -github.com/aws/aws-sdk-go v1.44.183 h1:mUk45JZTIMMg9m8GmrbvACCsIOKtKezXRxp06uI5Ahk= -github.com/aws/aws-sdk-go v1.44.183/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= -github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/aws/aws-sdk-go v1.44.288 h1:Ln7fIao/nl0ACtelgR1I4AiEw/GLNkKcXfCaHupUW5Q= +github.com/aws/aws-sdk-go v1.44.288/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/bshuster-repo/logrus-logstash-hook v1.0.2 h1:JYRWo+QGnQdedgshosug9hxpPYTB9oJ1ZZD3fY31alU= -github.com/bugsnag/bugsnag-go v2.1.2+incompatible h1:E7dor84qzwUO8KdCM68CZwq9QOSR7HXlLx3Wj5vui2s= +github.com/bshuster-repo/logrus-logstash-hook v1.1.0 h1:o2FzZifLg+z/DN1OFmzTWzZZx/roaqt8IPZCIVco8r4= +github.com/bshuster-repo/logrus-logstash-hook v1.1.0/go.mod h1:Q2aXOe7rNuPgbBtPCOzYyWDvKX7+FpxE5sRdvcPoui0= +github.com/bugsnag/bugsnag-go v2.2.0+incompatible h1:ewHS3GgD2fBtipwmaa4Gcfhum4PSAHIwVypmjXyp/Dg= +github.com/bugsnag/bugsnag-go v2.2.0+incompatible/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/bugsnag-go/v2 v2.2.0 h1:y4JJ6xNJiK4jbmq/BLXe09MGUNRp/r1Zpye6RKcPJJ8= +github.com/bugsnag/bugsnag-go/v2 v2.2.0/go.mod h1:Aoi1ax1kGbbkArShzXUQjxp6jM8gMh4qOtHLis/jY1E= github.com/bugsnag/panicwrap v1.3.4 h1:A6sXFtDGsgU/4BLf5JT0o5uYg3EeKgGx3Sfs+/uk3pU= +github.com/bugsnag/panicwrap v1.3.4/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 h1:7aWHqerlJ41y6FOsEUvknqgXnGmJyJSbjhAWq5pO4F8= -github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -113,103 +86,56 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= -github.com/distribution/distribution/v3 v3.0.0-20220907155224-78b9c98c5c31 h1:AqcwAyaSEkILnr/bLybFnM2i/+EW67JJIRs/4dWmzxo= -github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32PaqRpvoEkKBy5M= -github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE= -github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= -github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= +github.com/distribution/distribution/v3 v3.0.0-20230608105614-4501a6e06d3b h1:WZ/BHeaEGLhQTl2WHXcOt+JI5y9jfswnGdSUvLOAbdo= +github.com/distribution/distribution/v3 v3.0.0-20230608105614-4501a6e06d3b/go.mod h1:+fqBJ4vPYo4Uu1ZE4d+bUtTLRXfdSL3NvCZIZ9GHv58= +github.com/docker/cli v24.0.5+incompatible h1:WeBimjvS0eKdH4Ygx+ihVq1Q++xg36M/rMi4aXAvodc= +github.com/docker/cli v24.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY= +github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8= +github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= -github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= -github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= -github.com/falcosecurity/falcoctl v0.3.0-rc5 h1:X/hjafMg5pltIGdJsi8KqgVdQ1U3RyXJAbe42WRtkas= -github.com/falcosecurity/falcoctl v0.3.0-rc5/go.mod h1:wusqDBDAEQFDG3w3s9eIxzZRm7ZAkFOaiM+Kp3aLI/w= -github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= -github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/falcosecurity/falcoctl v0.6.1 h1:Klg3jHi/PL1Inw9DO9kGpzL6ka+TjI4oDl6kvm1I+VY= +github.com/falcosecurity/falcoctl v0.6.1/go.mod h1:4Hx4h3KtcaQzPKxvYn5S9x4IHxwd6QRK9Gu04HHNbhE= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-oauth2/oauth2/v4 v4.5.2 h1:CuZhD3lhGuI6aNLyUbRHXsgG2RwGRBOuCBfd4WQKqBQ= +github.com/go-oauth2/oauth2/v4 v4.5.2/go.mod h1:wk/2uLImWIa9VVQDgxz99H2GDbhmfi/9/Xr+GvkSUSQ= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -217,7 +143,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -233,17 +158,12 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= -github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws= +github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -252,14 +172,10 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -272,202 +188,99 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9SN1TigNLn9ZnF3W4SYRKq2gAHs= +github.com/google/pprof v0.0.0-20230602150820-91b7bce49751/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= -github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw= github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= +github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= +github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= -github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= -github.com/lithammer/fuzzysearch v1.1.5 h1:Ag7aKU08wp0R9QCfF4GoGST9HbmAIeLP7xwMrOBEp1c= -github.com/lithammer/fuzzysearch v1.1.5/go.mod h1:1R1LRNk7yKid1BaQkmuLQaHruxcC4HmAH30Dh61Ih1Q= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= +github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q= -github.com/opencontainers/distribution-spec/specs-go v0.0.0-20220620172159-4ab4752c3b86 h1:Oumw+lPnO8qNLTY2mrqPJZMoGExLi/0h/DdikoLTXVU= -github.com/opencontainers/distribution-spec/specs-go v0.0.0-20220620172159-4ab4752c3b86/go.mod h1:aA4vdXRS8E1TG7pLZOz85InHi3BiPdErh8IpJN6E0x4= +github.com/onsi/ginkgo/v2 v2.10.0 h1:sfUl4qgLdvkChZrWCYndY2EAu9BRIw1YphNAzy1VNWs= +github.com/onsi/ginkgo/v2 v2.10.0/go.mod h1:UDQOh5wbQUlMnkLfVaIUMtQ1Vus92oM+P2JX1aulgcE= +github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= +github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec= -github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/oras-project/artifacts-spec v1.0.0-rc.2 h1:9SMCNSxkJEHqWGDiMCuy6TXHgvjgwXGdXZZGXLKQvVE= -github.com/oras-project/artifacts-spec v1.0.0-rc.2/go.mod h1:Xch2aLzSwtkhbFFN6LUzTfLtukYvMMdXJ4oZ8O7BOdc= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0= +github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/oras-project/oras-credentials-go v0.3.0 h1:Bg1d9iAmgo50RlaIy2XI5MQs7qL00DB3R9Q4JRP1VWs= +github.com/oras-project/oras-credentials-go v0.3.0/go.mod h1:fFCebDQo0Do+gnM96uV9YUnRay0pwuRQupypvofsp4s= +github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= +github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI= github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg= github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE= @@ -475,112 +288,91 @@ github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEej github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= -github.com/pterm/pterm v0.12.45 h1:5HATKLTDjl9D74b0x7yiHzFI7OADlSXK3yHrJNhRwZE= -github.com/pterm/pterm v0.12.45/go.mod h1:hJgLlBafm45w/Hr0dKXxY//POD7CgowhePaG1sdPNBg= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/pterm/pterm v0.12.67 h1:5iB7ajIQROYfxYD7+sFJ4+KJhFJ+xn7QOVBm4s6RUF0= +github.com/pterm/pterm v0.12.67/go.mod h1:nFuT9ZVkkCi8o4L1dtWuYPwDQxggLh4C263qG5nTLpQ= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= -github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= -github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= +github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= +github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI= -github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= -github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/tidwall/btree v1.1.0 h1:5P+9WU8ui5uhmcg3SoPyTwoI0mVyZ1nps7YQzTZFkYM= +github.com/tidwall/btree v1.1.0/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4= +github.com/tidwall/buntdb v1.2.0 h1:8KOzf5Gg97DoCMSOgcwZjnM0FfROtq0fcZkPW54oGKU= +github.com/tidwall/buntdb v1.2.0/go.mod h1:XLza/dhlwzO6dc5o/KWor4kfZSt3BP8QV+77ZMKfI58= +github.com/tidwall/gjson v1.12.1 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo= +github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg= +github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/rtred v0.1.2 h1:exmoQtOLvDoO8ud++6LwVsAMTu0KPzLTUrMln8u1yu8= +github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ= +github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE= +github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940 h1:p7OofyZ509h8DmPLh8Hn+EIIZm/xYhdZHJ9GnXHdr6U= +github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.7 h1:4DTF1WOM2ZZS/xMOkTFBOcb6XiHu/PKn3rVo6dbewQE= +github.com/yvasiyarov/gorelic v0.0.7/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20160601141957-9c099fbc30e9 h1:AsFN8kXcCVkUFHyuzp1FtYbzp1nCO/H6+1uPSGEyPzM= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20160601141957-9c099fbc30e9/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= -go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc= -go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -591,6 +383,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= +golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -604,7 +398,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -615,15 +408,10 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -631,11 +419,9 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -646,27 +432,21 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -676,13 +456,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.2.0 h1:GtQkldQ9m7yvzCL1V+LrYow3Khe0eJH0w7RbX/VbaIU= -golang.org/x/oauth2 v0.2.0/go.mod h1:Cwn6afJ8jrQwYMxQDTpISoXmXW9I6qF6vDeuuoX3Ibs= +golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= +golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -693,33 +468,22 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220907140024-f12130a52804 h1:0SH2R3f1b1VmIMG7BXbEZCBUu2dKmHschSmjqGUrW8A= -golang.org/x/sync v0.0.0-20220907140024-f12130a52804/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -731,10 +495,7 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -743,72 +504,61 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -828,11 +578,9 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -841,12 +589,12 @@ golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E= +golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -870,9 +618,6 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -904,7 +649,6 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= @@ -912,18 +656,12 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -937,15 +675,9 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -958,40 +690,27 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= -gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= -gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= +gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= +gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -999,50 +718,10 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.24.3 h1:tt55QEmKd6L2k5DP6G/ZzdMQKvG5ro4H4teClqm0sTY= -k8s.io/api v0.24.3/go.mod h1:elGR/XSZrS7z7cSZPzVWaycpJuGIw57j9b95/1PdJNI= -k8s.io/apimachinery v0.24.3 h1:hrFiNSA2cBZqllakVYyH/VyEh4B581bQRmqATJSeQTg= -k8s.io/apimachinery v0.24.3/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= -k8s.io/cli-runtime v0.24.3 h1:O9YvUHrDSCQUPlsqVmaqDrueqjpJ7IO6Yas9B6xGSoo= -k8s.io/cli-runtime v0.24.3/go.mod h1:In84wauoMOqa7JDvDSXGbf8lTNlr70fOGpYlYfJtSqA= -k8s.io/client-go v0.24.3 h1:Nl1840+6p4JqkFWEW2LnMKU667BUxw03REfLAVhuKQY= -k8s.io/client-go v0.24.3/go.mod h1:AAovolf5Z9bY1wIg2FZ8LPQlEdKHjLI7ZD4rw920BJw= -k8s.io/code-generator v0.24.3/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w= -k8s.io/component-base v0.24.3 h1:u99WjuHYCRJjS1xeLOx72DdRaghuDnuMgueiGMFy1ec= -k8s.io/component-base v0.24.3/go.mod h1:bqom2IWN9Lj+vwAkPNOv2TflsP1PeVDIwIN0lRthxYY= -k8s.io/component-helpers v0.24.3/go.mod h1:/1WNW8TfBOijQ1ED2uCHb4wtXYWDVNMqUll8h36iNVo= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/gengo v0.0.0-20211129171323-c02415ce4185/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= -k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 h1:Gii5eqf+GmIEwGNKQYQClCayuJCe2/4fZUvF7VG99sU= -k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= -k8s.io/kubectl v0.24.3 h1:PqY8ho/S/KuE2/hCC3Iee7X+lOtARYo0LQsNzvV/edE= -k8s.io/kubectl v0.24.3/go.mod h1:PYLcvw96sC1NLbxZEDbdlOEd6/C76VIWjGmWV5QjSk0= -k8s.io/metrics v0.24.3/go.mod h1:p1M0lhMySWfhISkSd3HEj8xIgrVnJTK3PPhFq2rA3To= -k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc= -k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -oras.land/oras-go/v2 v2.0.0-rc.3 h1:O4GeIwJ9Ge7rbCkqa/M7DLrL55ww+ZEc+Rhc63OYitU= -oras.land/oras-go/v2 v2.0.0-rc.3/go.mod h1:PrY+cCglzK/DrQoJUtxbYVbL94ZHecVS3eJR01RglpE= +k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= +k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +oras.land/oras-go/v2 v2.2.1 h1:3VJTYqy5KfelEF9c2jo1MLSpr+TM3mX8K42wzZcd6qE= +oras.land/oras-go/v2 v2.2.1/go.mod h1:GeAwLuC4G/JpNwkd+bSZ6SkDMGaaYglt6YK2WvZP7uQ= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y= -sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= -sigs.k8s.io/kustomize/api v0.11.4 h1:/0Mr3kfBBNcNPOW5Qwk/3eb8zkswCwnqQxxKtmrTkRo= -sigs.k8s.io/kustomize/api v0.11.4/go.mod h1:k+8RsqYbgpkIrJ4p9jcdPqe8DprLxFUUO0yNOq8C+xI= -sigs.k8s.io/kustomize/cmd/config v0.10.6/go.mod h1:/S4A4nUANUa4bZJ/Edt7ZQTyKOY9WCER0uBS1SW2Rco= -sigs.k8s.io/kustomize/kustomize/v4 v4.5.4/go.mod h1:Zo/Xc5FKD6sHl0lilbrieeGeZHVYCA4BzxeAaLI05Bg= -sigs.k8s.io/kustomize/kyaml v0.13.6 h1:eF+wsn4J7GOAXlvajv6OknSunxpcOBQQqsnPxObtkGs= -sigs.k8s.io/kustomize/kyaml v0.13.6/go.mod h1:yHP031rn1QX1lr/Xd934Ri/xdVNG8BE2ECa78Ht/kEg= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/build/registry/index.go b/build/registry/index.go index 793eae591..47466d9a9 100644 --- a/build/registry/index.go +++ b/build/registry/index.go @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2023 The Falco Authors. @@ -20,7 +21,7 @@ import ( "path/filepath" "strings" - "github.com/falcosecurity/falcoctl/pkg/index" + "github.com/falcosecurity/falcoctl/pkg/index/index" "github.com/falcosecurity/falcoctl/pkg/oci" ) @@ -36,10 +37,11 @@ func pluginRulesToIndexEntry(rf Rulesfile, registry, repo string) *index.Entry { Repository: repo, Description: rf.Description, Home: rf.URL, - Keywords: append(rf.Keywords, rf.Name), + Keywords: appendIfNotPresent(rf.Keywords, rf.Name), License: rf.License, Maintainers: rf.Maintainers, Sources: []string{rf.URL}, + Signature: rf.Signature, } } @@ -69,3 +71,16 @@ func upsertIndexFile(r *Registry, ociArtifacts map[string]string, indexPath stri return i.Write(indexPath) } + +// Add new item to a slice if not present. +func appendIfNotPresent(keywords []string, kw string) []string { + // If the keyword already exist do nothing. + for i := range keywords { + if keywords[i] == kw { + return keywords + } + } + + // Add the keyword + return append(keywords, kw) +} diff --git a/build/registry/index_test.go b/build/registry/index_test.go index c0a96b0b8..cf11df880 100644 --- a/build/registry/index_test.go +++ b/build/registry/index_test.go @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2023 The Falco Authors. @@ -20,7 +21,7 @@ import ( "reflect" "testing" - "github.com/falcosecurity/falcoctl/pkg/index" + "github.com/falcosecurity/falcoctl/pkg/index/index" "github.com/stretchr/testify/assert" ) @@ -32,11 +33,25 @@ func Test_upsertIndex(t *testing.T) { indexPath string expectedIndexPath string }{ - {"missing", "testdata/registry.yaml", map[string]string{"falco": "ghcr.io/falcosecurity/rules/falco"}, "testdata/index1.yaml", "testdata/index_expected1.yaml"}, - {"already_present", "testdata/registry.yaml", map[string]string{"falco": "ghcr.io/falcosecurity/rules/falco"}, "testdata/index2.yaml", "testdata/index2.yaml"}, + { + "missing", + "testdata/registry.yaml", + map[string]string{"falco": "ghcr.io/falcosecurity/rules/falco"}, + "testdata/index1.yaml", + "testdata/index_expected1.yaml", + }, + { + "already_present", + "testdata/registry.yaml", + map[string]string{"falco": "ghcr.io/falcosecurity/rules/falco"}, + "testdata/index2.yaml", + "testdata/index2.yaml", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() + i := index.New(GHOrg) assert.NoError(t, i.Read(tt.indexPath)) expectedIndex := index.New(GHOrg) @@ -48,8 +63,25 @@ func Test_upsertIndex(t *testing.T) { upsertIndex(r, tt.ociArtifacts, i) if !reflect.DeepEqual(i, expectedIndex) { - t.Errorf("index() = %v, want %v", i, expectedIndex) + t.Errorf("index() = %#v, want %v", i, expectedIndex) } }) } } + +func TestPluginRulesToIndexEntrySignature(t *testing.T) { + t.Parallel() + + signature := &index.Signature{ + Cosign: &index.CosignSignature{}, + } + + expected := signature + + p := Rulesfile{Signature: signature} + + entry := pluginRulesToIndexEntry(p, "", "") + if !reflect.DeepEqual(entry.Signature, expected) { + t.Fatalf("Index entry signature: expected %#v, got %v", expected, entry.Signature) + } +} diff --git a/build/registry/oci.go b/build/registry/oci.go index 57ae1b049..c50c75b25 100644 --- a/build/registry/oci.go +++ b/build/registry/oci.go @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2023 The Falco Authors. @@ -27,19 +28,25 @@ import ( ocipusher "github.com/falcosecurity/falcoctl/pkg/oci/pusher" ) -func pushCompressedRulesfile(ociClient remote.Client, filePath, repoRef, repoGit string, tags []string, config *oci.ArtifactConfig) error { +// pushCompressedRulesfile publishes rulesfile as OCI artifact and returns its digest. +// It possibly returns an error. +func pushCompressedRulesfile( + ociClient remote.Client, + filePath, repoRef, repoGit string, + tags []string, + config *oci.ArtifactConfig) (*string, error) { klog.Infof("Processing compressed rulesfile %q for repo %q and tags %s...", filePath, repoRef, tags) pusher := ocipusher.NewPusher(ociClient, false, nil) - _, err := pusher.Push(context.Background(), oci.Rulesfile, repoRef, + artifact, err := pusher.Push(context.Background(), oci.Rulesfile, repoRef, ocipusher.WithTags(tags...), ocipusher.WithFilepaths([]string{filePath}), ocipusher.WithAnnotationSource(repoGit), ocipusher.WithArtifactConfig(*config)) if err != nil { - return fmt.Errorf("an error occurred while pushing: %w", err) + return nil, fmt.Errorf("an error occurred while pushing: %w", err) } - return nil + return &artifact.Digest, nil } diff --git a/build/registry/registry.go b/build/registry/registry.go index 8e6d38412..8d04dd85f 100644 --- a/build/registry/registry.go +++ b/build/registry/registry.go @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2023 The Falco Authors. @@ -21,6 +22,7 @@ import ( "os" "regexp" + "github.com/falcosecurity/falcoctl/pkg/index/index" "gopkg.in/yaml.v2" ) @@ -37,11 +39,13 @@ type Rulesfile struct { Email string `yaml:"email"` Name string `yaml:"name"` } `yaml:"maintainers"` - Keywords []string `yaml:"keywords"` - Path string `yaml:"path"` - URL string `yaml:"url"` - License string `yaml:"license"` - Reserved bool `yaml:"reserved"` + Keywords []string `yaml:"keywords"` + Path string `yaml:"path"` + URL string `yaml:"url"` + License string `yaml:"license"` + Reserved bool `yaml:"reserved"` + Archived bool `yaml:"archived"` + Signature *index.Signature `yaml:"signature,omitempty"` } type Registry struct { @@ -67,6 +71,9 @@ func (r *Registry) Validate() error { // RulesfileByName returns the rulesfile in the index with the specified name, or nil if not found func (r *Registry) RulesfileByName(name string) *Rulesfile { for _, rf := range r.Rulesfiles { + if rf.Reserved || rf.Archived { + continue + } if rf.Name == name { return &rf } diff --git a/build/registry/requirements.go b/build/registry/requirements.go index c8bb18d2a..6b3a7f649 100644 --- a/build/registry/requirements.go +++ b/build/registry/requirements.go @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2023 The Falco Authors. @@ -30,7 +31,7 @@ import ( const ( rulesEngineAnchor = "- required_engine_version" - engineVersionKey = "engine_version" + engineVersionKey = "engine_version_semver" ) // ErrReqNotFound error when the requirements are not found in the rulesfile. @@ -64,13 +65,23 @@ func rulesfileRequirement(filePath string) (*oci.ArtifactRequirement, error) { // Split the requirement and parse the version to semVer. tokens := strings.Split(fileScanner.Text(), ":") - reqVer, err := semver.ParseTolerant(tokens[1]) + version := strings.TrimSpace(tokens[1]) + reqVer, err := semver.Parse(version) if err != nil { - return nil, fmt.Errorf("unable to parse to semVer the version requirement %q", tokens[1]) + // If the version is not a valid semver, we try to parse it as a numeric value. + minor, err := strconv.ParseUint(version, 10, 64) + if err != nil { + return nil, fmt.Errorf("unable to parse requirement %q: expected a numeric value or a valid semver string", version) + } + reqVer = semver.Version{ + Major: 0, + Minor: minor, + Patch: 0, + } } return &oci.ArtifactRequirement{ Name: engineVersionKey, - Version: strconv.FormatUint(reqVer.Major, 10), + Version: reqVer.String(), }, nil } diff --git a/build/registry/requirements_test.go b/build/registry/requirements_test.go new file mode 100644 index 000000000..08f33cdc9 --- /dev/null +++ b/build/registry/requirements_test.go @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2024 The Falco Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestRulesFilesRequirement(t *testing.T) { + req, err := rulesfileRequirement("testdata/rules-failed-req.yaml") + assert.Error(t, err) + + req, err = rulesfileRequirement("testdata/rules-numeric-req.yaml") + assert.NoError(t, err) + assert.Equal(t, "0.15.0", req.Version) + assert.Equal(t, "engine_version_semver", req.Name) + + req, err = rulesfileRequirement("testdata/rules-semver-req.yaml") + assert.NoError(t, err) + assert.Equal(t, "0.31.0", req.Version) + assert.Equal(t, "engine_version_semver", req.Name) +} diff --git a/build/registry/s3.go b/build/registry/s3.go index eb78c3286..0c7ea5307 100644 --- a/build/registry/s3.go +++ b/build/registry/s3.go @@ -1,3 +1,20 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2023 The Falco Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package main import ( diff --git a/build/registry/tag.go b/build/registry/tag.go index 5a2487838..e031566bb 100644 --- a/build/registry/tag.go +++ b/build/registry/tag.go @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2023 The Falco Authors. @@ -100,22 +101,25 @@ func ociTagsToUpdate(newTag string, existingTags []string) []string { return tagsToUpdate } - var existingSemvers []semver.Version + var existingFinalSemvers []semver.Version for _, tag := range existingTags { if sv, err := semver.Parse(tag); err == nil { - existingSemvers = append(existingSemvers, sv) + // ignore prereleases + if len(sv.Pre) == 0 { + existingFinalSemvers = append(existingFinalSemvers, sv) + } } } - if isLatestSemverForMinor(newSemver, existingSemvers) { + if isLatestSemverForMinor(newSemver, existingFinalSemvers) { tagsToUpdate = append(tagsToUpdate, fmt.Sprintf("%d.%d", newSemver.Major, newSemver.Minor)) } - if isLatestSemverForMajor(newSemver, existingSemvers) { + if isLatestSemverForMajor(newSemver, existingFinalSemvers) { tagsToUpdate = append(tagsToUpdate, fmt.Sprintf("%d", newSemver.Major)) } - if isLatestSemver(newSemver, existingSemvers) { + if isLatestSemver(newSemver, existingFinalSemvers) { tagsToUpdate = append(tagsToUpdate, "latest") } diff --git a/build/registry/tag_test.go b/build/registry/tag_test.go index bb6443865..f2f07b370 100644 --- a/build/registry/tag_test.go +++ b/build/registry/tag_test.go @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2023 The Falco Authors. @@ -71,6 +72,8 @@ func Test_ociTagsToUpdate(t *testing.T) { {"latest_in_line", "0.1.3", []string{"0.1.2", "0.2.0", "0.3.1"}, []string{"0.1.3", "0.1"}}, {"version_1", "1.0.2", []string{"0.1.2", "0.2.0", "1.0.0", "2.0.0", "2.0.2"}, []string{"1", "1.0", "1.0.2"}}, {"prerelease", "0.1.4-rc1", []string{"0.1.2", "0.1.3"}, []string{"0.1.4-rc1"}}, + {"latest_with_prerelease", "1.0.2", []string{"1.0.0", "1.0.1", "2.0.0-rc1"}, []string{"1", "1.0", "1.0.2", "latest"}}, + {"not_latest_with_prerelease", "1.0.2", []string{"1.0.0", "1.0.1", "2.0.0-rc1", "2.0.0"}, []string{"1", "1.0", "1.0.2"}}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/build/registry/targz.go b/build/registry/targz.go index 55b9906bc..0de38b465 100644 --- a/build/registry/targz.go +++ b/build/registry/targz.go @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2023 The Falco Authors. diff --git a/build/registry/testdata/index2.yaml b/build/registry/testdata/index2.yaml index 598ca565c..cdc19485c 100644 --- a/build/registry/testdata/index2.yaml +++ b/build/registry/testdata/index2.yaml @@ -47,3 +47,7 @@ name: The Falco Authors sources: - https://github.com/falcosecurity/rules/blob/main/rules/falco_rules.yaml + signature: + cosign: + certificate-oidc-issuer: https://token.actions.githubusercontent.com + certificate-identity-regexp: https://github.com/falcosecurity/rules/ diff --git a/build/registry/testdata/index_expected1.yaml b/build/registry/testdata/index_expected1.yaml index 598ca565c..cdc19485c 100644 --- a/build/registry/testdata/index_expected1.yaml +++ b/build/registry/testdata/index_expected1.yaml @@ -47,3 +47,7 @@ name: The Falco Authors sources: - https://github.com/falcosecurity/rules/blob/main/rules/falco_rules.yaml + signature: + cosign: + certificate-oidc-issuer: https://token.actions.githubusercontent.com + certificate-identity-regexp: https://github.com/falcosecurity/rules/ diff --git a/build/registry/testdata/registry.yaml b/build/registry/testdata/registry.yaml index 14cd99525..4fc1573a4 100644 --- a/build/registry/testdata/registry.yaml +++ b/build/registry/testdata/registry.yaml @@ -8,7 +8,13 @@ rulesfiles: email: cncf-falco-dev@lists.cncf.io path: rules/falco_rules.yaml license: apache-2.0 + keywords: + - falco url: https://github.com/falcosecurity/rules/blob/main/rules/falco_rules.yaml + signature: + cosign: + certificate-oidc-issuer: https://token.actions.githubusercontent.com + certificate-identity-regexp: https://github.com/falcosecurity/rules/ - name: applications description: Application rules authors: The Falco Authors @@ -19,3 +25,7 @@ rulesfiles: path: rules/application_rules.yaml url: https://github.com/falcosecurity/rules/blob/main/rules/application_rules.yaml license: apache-2.0 + signature: + cosign: + certificate-oidc-issuer: https://token.actions.githubusercontent.com + certificate-identity-regexp: https://github.com/falcosecurity/rules/ diff --git a/build/registry/testdata/rules-failed-req.yaml b/build/registry/testdata/rules-failed-req.yaml new file mode 100644 index 000000000..ec939c4be --- /dev/null +++ b/build/registry/testdata/rules-failed-req.yaml @@ -0,0 +1 @@ +- required_engine_version: test \ No newline at end of file diff --git a/build/registry/testdata/rules-numeric-req.yaml b/build/registry/testdata/rules-numeric-req.yaml new file mode 100644 index 000000000..60b94c3ba --- /dev/null +++ b/build/registry/testdata/rules-numeric-req.yaml @@ -0,0 +1 @@ +- required_engine_version: 15 \ No newline at end of file diff --git a/build/registry/testdata/rules-semver-req.yaml b/build/registry/testdata/rules-semver-req.yaml new file mode 100644 index 000000000..56727d231 --- /dev/null +++ b/build/registry/testdata/rules-semver-req.yaml @@ -0,0 +1 @@ +- required_engine_version: 0.31.0 \ No newline at end of file diff --git a/docs/images/announce.png b/docs/images/announce.png new file mode 100644 index 000000000..bb604301d Binary files /dev/null and b/docs/images/announce.png differ diff --git a/docs/images/arrow.png b/docs/images/arrow.png new file mode 100644 index 000000000..3bd87afb2 Binary files /dev/null and b/docs/images/arrow.png differ diff --git a/docs/images/cross.png b/docs/images/cross.png new file mode 100644 index 000000000..e83c01e41 Binary files /dev/null and b/docs/images/cross.png differ diff --git a/docs/images/insight.png b/docs/images/insight.png new file mode 100644 index 000000000..4f1588ed9 Binary files /dev/null and b/docs/images/insight.png differ diff --git a/docs/images/setting.png b/docs/images/setting.png new file mode 100644 index 000000000..f9599fbca Binary files /dev/null and b/docs/images/setting.png differ diff --git a/docs/images/start.png b/docs/images/start.png new file mode 100644 index 000000000..051b5c5e4 Binary files /dev/null and b/docs/images/start.png differ diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 000000000..a90851db0 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,6 @@ +site_name: Falcosecurity Rules +site_url: https://github.com/falcosecurity/rules +nav: + - Home: index.md + +theme: material diff --git a/proposals/20230605-rules-adoption-management-maturity-framework.md b/proposals/20230605-rules-adoption-management-maturity-framework.md new file mode 100644 index 000000000..e906123bc --- /dev/null +++ b/proposals/20230605-rules-adoption-management-maturity-framework.md @@ -0,0 +1,245 @@ +# Falco Rules Adoption, Management and Maturity Framework + +## Objective + +The objective is to outline key enhancements and improvements to Falco, focusing on optimizing its rule adoption, customization, and management capabilities. The proposal also introduces a rules maturity framework to provide a structured approach for assessing and categorizing rules. + +In more detail, this proposal aims to address the expectations of adopters by providing clear guidelines for rule contribution, including processes and criteria. It also aims to provide guidance on rule customization and tuning to help adopters optimize the detection capabilities of Falco for their specific environments. Lastly, the proposal aims to empower adopters by providing them with the necessary knowledge and resources to effectively manage and customize rules. + +The proposed timeline for the initial completion of each item is the Falco 0.36 release, with further improvements being continuously added based on feedback. + + +## Current State + +This section highlights the current (as of June 6, 2023) procedures and resources for rules creation and adoption and serves as an assessment: + +- The Falco Project website provides detailed explanations on how to [write](https://falco.org/docs/rules/) Falco rules along with references such as the [supported fields](https://falco.org/docs/reference/rules/supported-fields/) for conditions and outputs. +- Community members contribute rules via opening a PR against the [falcosecurity/rules](https://github.com/falcosecurity/rules) repo. +- [Tutorials](https://falco.org/docs/tutorials/) and [blog posts](https://falco.org/tags/rules/) related to rules are available on the Falco website, along with numerous references to webinars and conference talks. +- Falco features a CLI tool called [falcoctl](https://github.com/falcosecurity/falcoctl) to manage the lifecycle of rules (installation, updates), see [blog post](https://falco.org/blog/falcoctl-install-manage-rules-plugins/). +- The rules framework of Falco is battle-tested and proven to be reliable in production environments. It is used by a diverse range of [organizations](https://github.com/falcosecurity/falco/blob/master/ADOPTERS.md). +- Existing rules yaml files + - Main Falco rules ([falco_rules.yaml](https://github.com/falcosecurity/rules/blob/main/rules/falco_rules.yaml)) based on syscall and container events. + - [application_rules.yaml](https://github.com/falcosecurity/rules/blob/main/rules/application_rules.yaml) also contains rules based on network-related syscalls, which may seem misleading if you expect those rules to be present in the regular "falco_rules.yaml" file. + - Plugins rules are based on third-party data sources that Falco hooks into, in addition to or instead of kernel tracing, e.g. [k8s_audit_rules.yaml](https://github.com/falcosecurity/plugins/blob/master/plugins/k8saudit/rules/k8s_audit_rules.yaml), [github.yaml](https://github.com/falcosecurity/plugins/blob/master/plugins/github/rules/github.yaml), [okta_rules.yaml](https://github.com/falcosecurity/plugins/blob/master/plugins/okta/rules/okta_rules.yaml) or [aws_cloudtrail_rules.yaml](https://github.com/falcosecurity/plugins/blob/master/plugins/cloudtrail/rules/aws_cloudtrail_rules.yaml). +- Rules include a concise description and a sense of `priority` to determine the level at which they should be loaded and activated. Furthermore, there is a key called `tag` that includes additional filter fields and information. This information may indicate whether a rule is designed to detect abnormal behavior in workloads running on the host or in containers. The tag field may also include details about the corresponding Mitre Attack phase and [TTP](https://attack.mitre.org/tactics/enterprise/) (Tactics, Techniques, and Procedures) codes. +- Falco provides an [overview](https://github.com/falcosecurity/rules/blob/main/rules_inventory/rules_overview.md) document that summarizes rules related to syscalls and container events and an experimental interactive [rules explorer website](https://github.com/Issif/falco-rules-explorer). +- Falco's website features [FAQ](https://falco.org/about/faq/) and [About](https://falco.org/about/) pages providing detailed technical information. + + +## Proposed Improvements + +### Highlight and Outline Primary Use Cases + +Adopters will be presented with clear primary use cases to better understand how Falco can be leveraged. These use cases will encompass "Threat Detection" and "Compliance". Rules specifically related to compliance will be tagged accordingly, based on their use case, similar to how we tag rules with the Mitre Attack phase. + +> Falco serves two main use cases: +> - Threat Detection: Rule violations as indicators of compromise. +> - Compliance: Detecting unauthorized changes to files under PCI/DSS. + + +### Rules Maturity Framework + +A rules maturity framework will be developed for Falco users to better facilitate the adoption of non-custom rules. This framework ensures a smooth transition for adopters, whether they use rules generically or for specific use cases. A smooth adoption process is defined by making it easy for adopters to understand each rule and also gain an understanding of not just what the rule is doing, but also how beneficial it can be under various circumstances. Additionally, adopters should have a clear idea of which rules can likely be adopted as-is versus which rules may require engineering efforts to evaluate and adopt. The rules maturity framework shall be accompanied by a guide that provides clear and unambiguous criteria for categorizing a rule that both the reviewer and contributor should adhere to. + +The rules maturity framework will align with the [status](https://github.com/falcosecurity/evolution/blob/main/REPOSITORIES.md#status) levels used within The Falco Project repositories, namely "Stable", "Incubating", "Sandbox" and "Deprecated". + +Not every rule has the potential to evolve and reach the "stable" level. This is because "stable" rules should address a broader category of attacks rather than being overly specific and easily bypassed. However, this does not mean that very specific rules do not provide value; on the contrary, they can serve a very specific purpose. The intention of the new framework is to make it clearer for adopters to recognize that they need to evaluate such rules for their own environment. More details about this will be explained in the respective maturity level section. + + +Levels (new Falco tags): + +- **maturity_stable** (enabled by default) indicates that the rule has undergone thorough evaluation by experts with hands-on production experience. These experts have determined that the rules embody best practices and exhibit optimal robustness, making it more difficult for attackers to bypass Falco. These rules are highly relevant for addressing broader threats and are recommended for customization to specific environments if necessary. They primarily focus on universal system-level detections, such as generic reverse shells or container escapes, which establish a solid baseline for threat detection across diverse industries. This inherent bias against including more application-specific detections is due to their potential lack of broad relevance or applicability. However, to mitigate this bias, a grey area will be reserved, enabling case-by-case judgments to be made. +- **maturity_incubating** (disabled by default) indicates that the rules address relevant threats, provide a certain level of robustness guarantee, and adhere to best practices in rule writing. Furthermore, it signifies that the rules have been identified by experts as catering to more specific use cases, which may or may not be relevant for each adopter. This category is expected to include a larger number of application-specific rules. +- **maturity_sandbox** (disabled by default) indicates that the rule is in an experimental stage. The potential for broader usefulness and relevance of "sandbox" rules is currently being assessed. These rules can serve as inspiration and adhere to the minimum acceptance criteria for rules. +- **maturity_deprecated** (disabled by default), indicates that, upon re-assessment, the rule was deemed less applicable to the broader community. Each adopter needs to determine the relevance of these rules on their own. They are kept as examples but are no longer actively supported or tuned by The Falco Project. + +> Falco introduces new additional tags, that is: +> +> `maturity_stable` +> `maturity_incubating` +> `maturity_sandbox` +> `maturity_deprecated` +> +> for each rule. This tag reflects the robustness, relevance, applicability, and stability of each predefined rule in the [falcosecurity/rules](https://github.com/falcosecurity/rules/blob/main/rules/) repository. It serves as general guidance to determine which rules may provide the highest return on investment. As a minimum requirement, each rule must go through the `maturity_incubating` state before advancing to `maturity_stable`. Only `maturity_stable` rules will be enabled by default. We use the existing tags mechanisms to build upon a battle-proven solution and will provide transparent configurability through the `falco.yaml` file to support the unique use cases of adopters. +> +> Furthermore, we will start by implementing tagging for the rules and establishing new guides. Subsequently, we will gather feedback from the community. In the future, we believe it would be beneficial to divide the rules files into separate entities: `falco_rules.yaml` (keeping the old established file name for the stable rules), `falco-incubating_rules.yaml`, `falco-sandbox_rules.yaml`, and `falco-deprecated_rules.yaml`. However, since this change could potentially disrupt some adopters and result in breaking changes to the order in which rules are loaded, we believe it is necessary to prioritize other improvements first. For instance, we may need to introduce features such as allowing multiple rules to match on a single event or providing the ability to customize the loading order, to name just a few. + +The maturity level of the rules, however, does not directly reflect their potential for generating noise in the adopters' environment. This is due to the unique and constantly changing nature of each environment, especially in cloud environments, making it challenging to accurately predict the impact of rules. + +Newcomers to Falco will be encouraged to start by configuring their setup with introductory rules labeled as "Falco's default rules" (`maturity_stable`). These rules, which are currently based on syscall and container events, will live in the established [falco_rules.yaml](https://github.com/falcosecurity/rules/blob/main/rules/falco_rules.yaml) file. + +As users become more familiar with Falco and better understand their unique environments, they can gradually fine-tune the default rules to meet their specific requirements. Tuning rules goes hand in hand with assessing the performance overhead and adjusting Falco's [configuration](https://github.com/falcosecurity/falco/blob/master/falco.yaml) accordingly. This consideration is crucial to convey to adopters, as it is important to keep in mind that there are usually limitations to the budget allocated for security monitoring. + +Once adopters have integrated the stable default rules with low false positives and acceptable performance consistently, they can add a next set of rules. This set may include rules with `maturity_incubating`, providing more specific detections. These efforts will be supported by guidance on rule customization and broader educational initiatives that focus on Linux OS runtime security. + +> Experts in the field, with expertise in areas such as offensive security, Linux kernel tracing, production deployment, cyber security, threat detection, compliance, data analysis, and data science, will be responsible for assessing the maturity levels of rules. These experts have a deep familiarity with Falco. + + +*Disclaimer*: The Falco maintainers understand that this approach may not be perfect from the beginning and that it requires field experience and community feedback to move towards a desired end solution. We have chosen the outlined rules maturity tagging approach as an initial solution to introduce more clarity and flexibility than we currently have while still aiming to keep things relatively simple in the beginning. + + +### Define Rule Contribution Process + +A guide will be established to facilitate the contribution of new rules or updates to existing ones. This guide will serve as an extension to the generic Falco [contributing guide](https://github.com/falcosecurity/.github/blob/main/CONTRIBUTING.md). This guide will outline clear acceptance criteria for each maturity level and establish minimum requirements for rule creation. These criteria will be utilized to assess the acceptance or rejection of a rule. + +This guide will also include information on how the community is intended to share rules. As a general principle, the project's main objective is to curate an optimal set of "stable" rules that are included in the main [falco_rules.yaml](https://github.com/falcosecurity/rules/blob/main/rules/falco_rules.yaml) file and also published as OCI artifacts. However, there may be opportunities to create additional platforms for ad-hoc rules sharing that do not necessarily need to meet the strict criteria set by the Falco maintainers. + + +The minimum criteria for rules creation shall include: + +*Correctness* + +The criteria with regard to correctness shall include, at a minimum: + +- Correctness of the expression language, both syntactically and grammatically. +- Consistency with the name/description. +- If any tests are present, they must pass. During the initial review process and subsequent changes, manual testing should also be conducted to verify that the rule is capable of detecting the cyber threat(s) it aims to detect (read more under the testing section). + + +*Robustness* + +To enhance the effectiveness of detection, priority is given to behavioral detections, as opposed to simple string matching on process command arguments or other fields. This preference is based on the ease with which the latter can be circumvented. The same principle applies when selecting the most robust system call for detecting a specific threat at a particular point in time or attack scenario. For concrete examples of more robust rules, please refer to Appendix 1. + + +*Relevance* + +Determining relevance is often the most subjective criterion, as it requires expertise in offensive security, cyber defense, and real-world production settings for accurate assessment. Questions such as whether these threats are a priority for most organizations or if we can provide enough context for a security analyst to appropriately act on the alerts as part of their incident response workflows are top of mind when assessing the overall relevance. Relevance is a key factor that indirectly reflects both robustness and significance, but more importantly, it indicates whether a particular security threat is significant to most adopters and, consequently, beneficial to detect. + +Possible criteria: + +- Cover relevant attack vectors across various industries. +- Emphasize profiling over signatures. +- Effectiveness of the rule across diverse workloads. +- Guidance and templates are provided to assist with tuning. + +*Testing* + +How was the rule tested? The desired testing approach includes not only functional end-to-end tests in virtual machines (VMs), but also deployments in real-world infrastructure. The Falco maintainers will provide guidance and support to contributors throughout the testing process, recognizing that it will vary significantly for each rule due to its unique nature. Additionally, while we aim to automate tests and expand test coverage, we acknowledge that it is a large engineering effort that will take time. Therefore, manual tests during the review processes and best effort deployment tests will be necessary in the interim. + + +### Guidance on Rule Customization and Tuning + +Each rule tagged as "Stable" will provide clear guidance on how it can be tuned, customized, or combined with other rules if applicable. Over time, a catalog of general best tuning practices will be developed. For specific examples, please refer to Appendix 2. + + +### Setting Expectations for Adopters + +Setting clear expectations for adopters of Falco is crucial. While Falco is a powerful security monitoring tool, it's important to be aware of its limitations and maintain realistic expectations. Please refer to Appendix 3 for specific examples of current limitations of Falco. + +Adopters should understand the importance of consulting the latest [documentation](https://falco.org/) and updates to ensure they have accurate and up-to-date information. Additionally, the project [roadmap](https://github.com/orgs/falcosecurity/projects/5) offers valuable insights into features that are currently being developed or planned for future releases. Keeping informed about the roadmap helps adopters understand the direction of Falco's development and the potential enhancements that may be available in the future for writing more powerful detections. + +In addition, effectively utilizing Falco requires expertise in various domains, including offensive security, Linux kernel tracing, production deployment, cyber security, threat detection, compliance, data analysis, and data science. Depending on the specific domain it may be easier to get started or require a little ramping up. + +## Key Results for Falco 0.36 + +In summary, the following action items are planned to be completed leading up to the Falco 0.36 release: + +- Clearly communicate Falco's primary uses: threat detection and compliance. +- Establish specific criteria for each rules maturity level and define general criteria for creating, contributing, and updating rules based on their respective maturity levels. +- Create a clear PR template for the rules repo that references the criteria for rules acceptance. +- Offer guidance and templates for rules tuning purposes. +- Audit each existing rule and assign a maturity level to it. Ensure a minimum set of 20 or more diverse rules are identified as "stable" and enabled by default. These rules should cover a wide range of top cyber threats and have the potential to effectively detect indicators of compromise related to those threats. +- Audit the existing rules from a compliance perspective and tag them with the compliance related tag if applicable. By the release of Falco 0.36, ensure that there are at least three template Falco rules in the "incubating" state specifically designed for "compliance" use cases. +- Add support for the new maturity tags in the Falco binary and configuration file. +- Update Falco's website to concisely document new processes and information, making it a single source of truth for creating, tuning, or contributing rules. + +As a result of these changes, Falco's principles for rules adoption, maturity, and management will be updated. However, adopters can continue to use Falco as they have been doing before. + +*Disclaimer*: If new information becomes available during the implementation of these changes, such as expanded feedback from the community, the Falco maintainers reserve the right to be flexible and adaptive, and re-evaluate choices and designs. + +## Appendix + + +### Appendix 1 + +Here are some examples of more robust rules with a brief description of why they are considered more robust and behavioral-based rather than relying too heavily on signatures: + +
+ Detect release_agent File Container Escapes +

+ Detecting attempts to escape a container is a crucial detection for modern container orchestration systems like Kubernetes. This rule stands out due to its inclusion of preconditions, which verify the necessary privileges of the container. Without these preconditions, the specific TTPs associated with container escape are not feasible. +

+

+ The rule is based on the open syscall while monitoring file writing activities, specifically looking for a string match on the file name "release_agent". This approach is robust because Linux expects the cgroup's release_agent file to be named in this manner. +

+

+ One downside of the rule is that it addresses only one specific TTP. Enabling additional rules like "Change thread namespace" can enhance coverage for other container escape methods. +

+
+ + +
+ Drop and execute new binary in container +

+ The Falco 0.34 release note provides a concise summary of how high-value kernel signals can greatly simplify the task of detecting suspicious executions that occur when a malicious implant is dropped and executed. Instead of relying on complex checks for executable paths, the focus shifts to identifying executables that were not part of the container image and were executed from the container's upper overlayfs layer shortly after being dropped. +

+

+ This approach narrows detection scope, increases tractability, and eliminates the need for inspecting unfamiliar or unusual executable paths. By leveraging high-value kernel signals, detections become more precise, removing ambiguity and providing crucial context. +

+
+ +
+ + +### Appendix 2 + +Tuning Falco rules can vary in complexity and involve tradeoffs based on the environment. While some tuning processes are straightforward, there are cases that require nuanced adjustments. It's essential for adopters to be aware that on-host rule tuning may have limitations. + +In some scenarios, adopters may benefit from acknowledging that operationalizing detections could require additional data analysis and correlation. This implies that adopting organizations may need to leverage data lake systems for further analysis and contextual correlation of the generated alerts, going beyond rule tuning. + +Here are some general tricks for tuning on-host rules: + +
+ Profiling +

+ Profiling the environment can be effective in detecting abnormal behaviors that may be considered normal in system-critical applications but outliers in standard applications. Implementing a simple allow list, such as for container names or namespaces, can already provide valuable assistance. This approach also aligns with the practice of clearly defining the crown jewel applications for which robust detections are desired. +

+
+ +
+ Linux concepts / behavioral indicators +

+ Another aspect of tuning a detection involves considering behavioral aspects related to Linux concepts. For example, a detection can be tuned based on the presence of a shell or a Java process in the parent process lineage, or detecting file manipulations while maintaining manual interactive shell access to a container. By incorporating these behavioral indicators, the detection can become more specific, relevant, and effective in identifying potential security threats. +

+
+ +
+ + +### Appendix 3 + +Examples of existing gaps in Falco for threat detection, as of June 6, 2023, include: + +
+ Deep kernel-level monitoring +

+ Falco operates at the kernel level but does not provide deep visibility into all aspects of kernel internals. It focuses on monitoring system calls and other observable events but may not capture low-level kernel activities. +

+
+ +
+ Network packet inspection +

+ Falco's primary focus is on monitoring system calls, and while it can detect network-related system calls, it may not offer extensive network packet inspection capabilities. Additionally, when considering modern cloud architectures with load balancers in front of application backend servers, there are inherent limitations in L3/4 network monitoring. Lastly, keeping in mind that Falco runs on each host in isolation, it means that certain correlations and detailed network introspection are still being extended and improved upon. +

+
+ +
+ Full-stack application monitoring +

+ Essentially, Falco is designed to monitor the Linux kernel and system-level activities. While it can capture certain application-related events, it may not provide comprehensive monitoring and visibility into the full application stack. However, there are exceptions where Falco has expanded its monitoring coverage to additional data sources using the plugins framework. One notable example is the integration with Kubernetes audit logs, which provides monitoring at the control plane level within a Kubernetes infrastructure. In addition, Falco's underlying libraries possess the capability to capture abnormal behavior at higher levels of the stack, such as analyzing HTTP requests. However, this potential is currently not exposed in Falco. +

+
+ + +
+ Advanced behavior analysis / anomaly detection +

+ Falco excels at detecting known patterns and rules-based anomalies. However, it currently may have limitations when it comes to advanced behavior analysis, on host anomaly detection, or identifying zero-day exploits that do not exhibit known patterns. +

+
+ +
+ +Consult the project [roadmap](https://github.com/orgs/falcosecurity/projects/5) and official [documentation](https://falco.org/) for up to date information on Falco's current capabilities. diff --git a/registry.yaml b/registry.yaml index 8178f8a30..23b57b88f 100644 --- a/registry.yaml +++ b/registry.yaml @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 # # Copyright (C) 2023 The Falco Authors. # @@ -15,7 +16,6 @@ # limitations under the License. # - rulesfiles: - name: falco-rules description: Falco rules that are loaded by default @@ -27,13 +27,56 @@ rulesfiles: path: rules/falco_rules.yaml license: apache-2.0 url: https://github.com/falcosecurity/rules/blob/main/rules/falco_rules.yaml + signature: + cosign: + certificate-oidc-issuer: https://token.actions.githubusercontent.com + certificate-identity-regexp: https://github.com/falcosecurity/rules/ + - name: falco-incubating-rules + description: Falco incubating rules + authors: The Falco Authors + contact: https://falco.org/community + maintainers: + - name: The Falco Authors + email: cncf-falco-dev@lists.cncf.io + path: rules/falco-incubating_rules.yaml + license: apache-2.0 + url: https://github.com/falcosecurity/rules/blob/main/rules/falco-incubating_rules.yaml + signature: + cosign: + certificate-oidc-issuer: https://token.actions.githubusercontent.com + certificate-identity-regexp: https://github.com/falcosecurity/rules/ + - name: falco-sandbox-rules + description: Falco sandbox rules + authors: The Falco Authors + contact: https://falco.org/community + maintainers: + - name: The Falco Authors + email: cncf-falco-dev@lists.cncf.io + path: rules/falco-sandbox_rules.yaml + license: apache-2.0 + url: https://github.com/falcosecurity/rules/blob/main/rules/falco-sandbox_rules.yaml + signature: + cosign: + certificate-oidc-issuer: https://token.actions.githubusercontent.com + certificate-identity-regexp: https://github.com/falcosecurity/rules/ + - name: falco-deprecated-rules + description: Falco deprecated rules kept as examples but are no longer maintained + authors: The Falco Authors + contact: https://falco.org/community + maintainers: + - name: The Falco Authors + email: cncf-falco-dev@lists.cncf.io + path: rules/falco-deprecated_rules.yaml + license: apache-2.0 + url: https://github.com/falcosecurity/rules/blob/main/rules/falco-deprecated_rules.yaml - name: application-rules - description: Application rules + archived: true + description: This rules files has been archived and is no longer maintained authors: The Falco Authors contact: https://falco.org/community maintainers: - name: The Falco Authors email: cncf-falco-dev@lists.cncf.io - path: rules/application_rules.yaml - url: https://github.com/falcosecurity/rules/blob/main/rules/application_rules.yaml + path: archive/application_rules.yaml + url: https://github.com/falcosecurity/rules/blob/main/archive/application_rules.yaml license: apache-2.0 diff --git a/rules/OWNERS b/rules/OWNERS new file mode 100644 index 000000000..de1c78292 --- /dev/null +++ b/rules/OWNERS @@ -0,0 +1,2 @@ +approvers: + - darryk10 diff --git a/rules/falco-deprecated_rules.yaml b/rules/falco-deprecated_rules.yaml new file mode 100644 index 000000000..57730b6f8 --- /dev/null +++ b/rules/falco-deprecated_rules.yaml @@ -0,0 +1,196 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (C) 2023 The Falco Authors. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Information about rules tags and fields can be found here: https://falco.org/docs/rules/#tags-for-current-falco-ruleset +# The initial item in the `tags` fields reflects the maturity level of the rules introduced upon the proposal https://github.com/falcosecurity/rules/blob/main/proposals/20230605-rules-adoption-management-maturity-framework.md +# `tags` fields also include information about the type of workload inspection (host and/or container), and Mitre Attack killchain phases and Mitre TTP code(s) +# Mitre Attack References: +# [1] https://attack.mitre.org/tactics/enterprise/ +# [2] https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json + +# Starting with version 8, the Falco engine supports exceptions. +# However the Falco rules file does not use them by default. +- required_engine_version: 0.31.0 + +# This macro `never_true` is used as placeholder for tuning negative logical sub-expressions, for example +# - macro: allowed_ssh_hosts +# condition: (never_true) +# can be used in a rules' expression with double negation `and not allowed_ssh_hosts` which effectively evaluates +# to true and does nothing, the perfect empty template for `logical` cases as opposed to list templates. +# When tuning the rule you can override the macro with something useful, e.g. +# - macro: allowed_ssh_hosts +# condition: (evt.hostname contains xyz) +- macro: never_true + condition: (evt.num=0) + +# RFC1918 addresses were assigned for private network usage +- list: rfc_1918_addresses + items: ['"10.0.0.0/8"', '"172.16.0.0/12"', '"192.168.0.0/16"'] + +- macro: outbound + condition: > + (((evt.type = connect and evt.dir=<) or + (evt.type in (sendto,sendmsg) and evt.dir=< and + fd.l4proto != tcp and fd.connected=false and fd.name_changed=true)) and + (fd.typechar = 4 or fd.typechar = 6) and + (fd.ip != "0.0.0.0" and fd.net != "127.0.0.0/8" and not fd.snet in (rfc_1918_addresses)) and + (evt.rawres >= 0 or evt.res = EINPROGRESS)) + +# Very similar to inbound/outbound, but combines the tests together +# for efficiency. +- macro: inbound_outbound + condition: > + ((((evt.type in (accept,accept4,listen,connect) and evt.dir=<)) and + (fd.typechar = 4 or fd.typechar = 6)) and + (fd.ip != "0.0.0.0" and fd.net != "127.0.0.0/8") and + (evt.rawres >= 0 or evt.res = EINPROGRESS)) + +- macro: ssh_port + condition: (fd.sport=22) + +# In a local/user rules file, you could override this macro to +# enumerate the servers for which ssh connections are allowed. For +# example, you might have a ssh gateway host for which ssh connections +# are allowed. +# +# In the main falco rules file, there isn't any way to know the +# specific hosts for which ssh access is allowed, so this macro just +# repeats ssh_port, which effectively allows ssh from all hosts. In +# the overridden macro, the condition would look something like +# "fd.sip="a.b.c.d" or fd.sip="e.f.g.h" or ..." +- macro: allowed_ssh_hosts + condition: (never_true) + +- rule: Disallowed SSH Connection + desc: > + Detect any new SSH connection on port 22 to a host other than those in an allowed list of hosts. + This rule absolutely requires profiling your environment beforehand. Network-based rules are extremely + crucial in any security program, as they can often provide the only definitive evidence. However, + effectively operationalizing them can be challenging due to the potential for noise. + condition: > + inbound_outbound + and ssh_port + and not allowed_ssh_hosts + enabled: false + output: Disallowed SSH Connection (connection=%fd.name lport=%fd.lport rport=%fd.rport fd_type=%fd.type fd_proto=fd.l4proto evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: NOTICE + tags: [maturity_deprecated, host, container, network, mitre_lateral_movement, T1021.004] + +# These rules and supporting macros are more of an example for how to +# use the fd.*ip and fd.*ip.name fields to match connection +# information against ips, netmasks, and complete domain names. +# +# To use this rule, you should enable it and +# populate allowed_{source,destination}_{ipaddrs,networks,domains} with the +# values that make sense for your environment. + +# Note that this can be either individual IPs or netmasks +- list: allowed_outbound_destination_ipaddrs + items: ['"127.0.0.1"', '"8.8.8.8"'] + +- list: allowed_outbound_destination_networks + items: ['"127.0.0.1/8"'] + +- list: allowed_outbound_destination_domains + items: [google.com, www.yahoo.com] + +- rule: Unexpected outbound connection destination + desc: > + Detect any outbound connection to a destination outside of an allowed set of ips, networks, or domain names. + This rule absolutely requires profiling your environment beforehand. Network-based rules are extremely crucial + in any security program, as they can often provide the only definitive evidence. However, effectively operationalizing + them can be challenging due to the potential for noise. + condition: > + outbound + and not ((fd.sip in (allowed_outbound_destination_ipaddrs)) or + (fd.snet in (allowed_outbound_destination_networks)) or + (fd.sip.name in (allowed_outbound_destination_domains))) + enabled: false + output: Disallowed outbound connection destination (connection=%fd.name lport=%fd.lport rport=%fd.rport fd_type=%fd.type fd_proto=fd.l4proto evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: NOTICE + tags: [maturity_deprecated, host, container, network, mitre_command_and_control, TA0011] +# Use this to test whether the event occurred within a container. +# When displaying container information in the output field, use +# %container.info, without any leading term (file=%fd.name +# %container.info user=%user.name user_loginuid=%user.loginuid, and not file=%fd.name +# container=%container.info user=%user.name user_loginuid=%user.loginuid). The output will change +# based on the context and whether or not -pk/-pm/-pc was specified on +# the command line. +- macro: container + condition: (container.id != host) + +- list: allowed_image + items: [] # add image to monitor, i.e.: bitnami/nginx + +- list: authorized_server_binary + items: [] # add binary to allow, i.e.: nginx + +- list: authorized_server_port + items: [] # add port to allow, i.e.: 80 + +# # How to test: +# kubectl run --image=nginx nginx-app --port=80 --env="DOMAIN=cluster" +# kubectl expose deployment nginx-app --port=80 --name=nginx-http --type=LoadBalancer +# # On minikube: +# minikube service nginx-http +# # On general K8s: +# kubectl get services +# kubectl cluster-info +# # Visit the Nginx service and port, should not fire. +# # Change rule to different port, then different process name, and test again that it fires. + +- rule: Outbound or Inbound Traffic not to Authorized Server Process and Port + desc: > + Detect traffic to an unauthorized server process and port within pre-defined containers. + This rule absolutely requires profiling your environment beforehand and also necessitates adjusting the list of containers + to which this rule will be applied. The current expression logic will never evaluate to true unless the list is populated. + Network-based rules are extremely crucial in any security program, as they can often provide the only definitive evidence. + However, effectively operationalizing them can be challenging due to the potential for noise. Notably, this rule is challenging + to operationalize. + condition: > + inbound_outbound + and container + and container.image.repository in (allowed_image) + and not proc.name in (authorized_server_binary) + and not fd.sport in (authorized_server_port) + enabled: false + output: Network connection outside authorized port and binary (connection=%fd.name lport=%fd.lport rport=%fd.rport fd_type=%fd.type fd_proto=fd.l4proto evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: WARNING + tags: [maturity_deprecated, container, network, mitre_discovery, TA0011, NIST_800-53_CM-7] + +- list: c2_server_ip_list + items: [] + +- list: c2_server_fqdn_list + items: [] + +- rule: Outbound Connection to C2 Servers + desc: > + Detect outbound connections to command and control servers using a list of IP addresses and fully qualified domain names (FQDNs). + This rule absolutely requires profiling your environment beforehand and also necessitates adjusting the template lists. The current + expression logic will never evaluate to true unless the lists are populated. Network-based rules are extremely crucial in any + security program, as they can often provide the only definitive evidence. However, effectively operationalizing them can be challenging + due to the potential for noise. Notably, this rule is challenging to operationalize. + condition: > + outbound + and ((fd.sip in (c2_server_ip_list)) or + (fd.sip.name in (c2_server_fqdn_list))) + output: Outbound connection to C2 server (c2_domain=%fd.sip.name c2_addr=%fd.sip connection=%fd.name lport=%fd.lport rport=%fd.rport fd_type=%fd.type fd_proto=fd.l4proto evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: WARNING + enabled: false + tags: [maturity_deprecated, host, container, network, mitre_command_and_control, TA0011] diff --git a/rules/falco-incubating_rules.yaml b/rules/falco-incubating_rules.yaml new file mode 100644 index 000000000..58afc4911 --- /dev/null +++ b/rules/falco-incubating_rules.yaml @@ -0,0 +1,1307 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (C) 2023 The Falco Authors. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Information about rules tags and fields can be found here: https://falco.org/docs/rules/#tags-for-current-falco-ruleset +# The initial item in the `tags` fields reflects the maturity level of the rules introduced upon the proposal https://github.com/falcosecurity/rules/blob/main/proposals/20230605-rules-adoption-management-maturity-framework.md +# `tags` fields also include information about the type of workload inspection (host and/or container), and Mitre Attack killchain phases and Mitre TTP code(s) +# Mitre Attack References: +# [1] https://attack.mitre.org/tactics/enterprise/ +# [2] https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json + +# Starting with version 8, the Falco engine supports exceptions. +# However the Falco rules file does not use them by default. +- required_engine_version: 0.35.0 + +- macro: open_write + condition: (evt.type in (open,openat,openat2) and evt.is_open_write=true and fd.typechar='f' and fd.num>=0) + +- macro: open_read + condition: (evt.type in (open,openat,openat2) and evt.is_open_read=true and fd.typechar='f' and fd.num>=0) + +- macro: open_directory + condition: (evt.type in (open,openat,openat2) and evt.is_open_read=true and fd.typechar='d' and fd.num>=0) + +# This macro `never_true` is used as placeholder for tuning negative logical sub-expressions, for example +# - macro: allowed_ssh_hosts +# condition: (never_true) +# can be used in a rules' expression with double negation `and not allowed_ssh_hosts` which effectively evaluates +# to true and does nothing, the perfect empty template for `logical` cases as opposed to list templates. +# When tuning the rule you can override the macro with something useful, e.g. +# - macro: allowed_ssh_hosts +# condition: (evt.hostname contains xyz) +- macro: never_true + condition: (evt.num=0) + +# This macro `always_true` is the flip side of the macro `never_true` and currently is commented out as +# it is not used. You can use it as placeholder for a positive logical sub-expression tuning template +# macro, e.g. `and custom_procs`, where +# - macro: custom_procs +# condition: (always_true) +# later you can customize, override the macros to something like +# - macro: custom_procs +# condition: (proc.name in (custom1, custom2, custom3)) +# - macro: always_true +# condition: (evt.num>=0) + +# In some cases, such as dropped system call events, information about +# the process name may be missing. For some rules that really depend +# on the identity of the process performing an action such as opening +# a file, etc., we require that the process name be known. +- macro: proc_name_exists + condition: (not proc.name in ("","N/A")) + +- macro: rename + condition: (evt.type in (rename, renameat, renameat2)) + +- macro: remove + condition: (evt.type in (rmdir, unlink, unlinkat)) + +- macro: modify + condition: (rename or remove) + +- macro: spawned_process + condition: (evt.type in (execve, execveat) and evt.dir=<) + +- macro: chmod + condition: (evt.type in (chmod, fchmod, fchmodat) and evt.dir=<) + +- list: shell_binaries + items: [ash, bash, csh, ksh, sh, tcsh, zsh, dash] + +- list: ssh_binaries + items: [ + sshd, sftp-server, ssh-agent, + ssh, scp, sftp, + ssh-keygen, ssh-keysign, ssh-keyscan, ssh-add + ] + +- list: coreutils_binaries + items: [ + truncate, sha1sum, numfmt, fmt, fold, uniq, cut, who, + groups, csplit, sort, expand, printf, printenv, unlink, tee, chcon, stat, + basename, split, nice, "yes", whoami, sha224sum, hostid, users, stdbuf, + base64, unexpand, cksum, od, paste, nproc, pathchk, sha256sum, wc, test, + comm, arch, du, factor, sha512sum, md5sum, tr, runcon, env, dirname, + tsort, join, shuf, install, logname, pinky, nohup, expr, pr, tty, timeout, + tail, "[", seq, sha384sum, nl, head, id, mkfifo, sum, dircolors, ptx, shred, + tac, link, chroot, vdir, chown, touch, ls, dd, uname, "true", pwd, date, + chgrp, chmod, mktemp, cat, mknod, sync, ln, "false", rm, mv, cp, echo, + readlink, sleep, stty, mkdir, df, dir, rmdir, touch + ] + +# dpkg -L login | grep bin | xargs ls -ld | grep -v '^d' | awk '{print $9}' | xargs -L 1 basename | tr "\\n" "," +- list: login_binaries + items: [ + login, systemd, '"(systemd)"', systemd-logind, su, + nologin, faillog, lastlog, newgrp, sg + ] + +# dpkg -L passwd | grep bin | xargs ls -ld | grep -v '^d' | awk '{print $9}' | xargs -L 1 basename | tr "\\n" "," +- list: passwd_binaries + items: [ + shadowconfig, grpck, pwunconv, grpconv, pwck, + groupmod, vipw, pwconv, useradd, newusers, cppw, chpasswd, usermod, + groupadd, groupdel, grpunconv, chgpasswd, userdel, chage, chsh, + gpasswd, chfn, expiry, passwd, vigr, cpgr, adduser, addgroup, deluser, delgroup + ] + +# repoquery -l shadow-utils | grep bin | xargs ls -ld | grep -v '^d' | +# awk '{print $9}' | xargs -L 1 basename | tr "\\n" "," +- list: shadowutils_binaries + items: [ + chage, gpasswd, lastlog, newgrp, sg, adduser, deluser, chpasswd, + groupadd, groupdel, addgroup, delgroup, groupmems, groupmod, grpck, grpconv, grpunconv, + newusers, pwck, pwconv, pwunconv, useradd, userdel, usermod, vigr, vipw, unix_chkpwd + ] + +- list: sysdigcloud_binaries + items: [setup-backend, dragent, sdchecks] + +- list: k8s_binaries + items: [hyperkube, skydns, kube2sky, exechealthz, weave-net, loopback, bridge, openshift-sdn, openshift] + +- list: lxd_binaries + items: [lxd, lxcfs] + +- list: db_server_binaries + items: [mysqld, postgres, sqlplus] + +# The explicit quotes are needed to avoid the - characters being +# interpreted by the filter expression. +- list: rpm_binaries + items: [dnf, dnf-automatic, rpm, rpmkey, yum, '"75-system-updat"', rhsmcertd-worke, rhsmcertd, subscription-ma, + repoquery, rpmkeys, rpmq, yum-cron, yum-config-mana, yum-debug-dump, + abrt-action-sav, rpmdb_stat, microdnf, rhn_check, yumdb] + +- list: deb_binaries + items: [dpkg, dpkg-preconfigu, dpkg-reconfigur, dpkg-divert, apt, apt-get, aptitude, + frontend, preinst, add-apt-reposit, apt-auto-remova, apt-key, + apt-listchanges, unattended-upgr, apt-add-reposit, apt-cache, apt.systemd.dai + ] +- list: python_package_managers + items: [pip, pip3, conda] + +# The truncated dpkg-preconfigu is intentional, process names are +# truncated at the falcosecurity-libs level. +- list: package_mgmt_binaries + items: [rpm_binaries, deb_binaries, update-alternat, gem, npm, python_package_managers, sane-utils.post, alternatives, chef-client, apk, snapd] + +- macro: package_mgmt_procs + condition: (proc.name in (package_mgmt_binaries)) + +- macro: package_mgmt_ancestor_procs + condition: (proc.pname in (package_mgmt_binaries) or + proc.aname[2] in (package_mgmt_binaries) or + proc.aname[3] in (package_mgmt_binaries) or + proc.aname[4] in (package_mgmt_binaries)) + +# A canonical set of processes that run other programs with different +# privileges or as a different user. +- list: userexec_binaries + items: [sudo, su, suexec, critical-stack, dzdo] + +- list: known_setuid_binaries + items: [ + sshd, dbus-daemon-lau, ping, ping6, critical-stack-, pmmcli, + filemng, PassengerAgent, bwrap, osdetect, nginxmng, sw-engine-fpm, + start-stop-daem + ] + +- list: user_mgmt_binaries + items: [login_binaries, passwd_binaries, shadowutils_binaries] + +- list: dev_creation_binaries + items: [blkid, rename_device, update_engine, sgdisk] + +- list: nomachine_binaries + items: [nxexec, nxnode.bin, nxserver.bin, nxclient.bin] + +- macro: system_procs + condition: (proc.name in (coreutils_binaries, user_mgmt_binaries)) + +- list: mail_binaries + items: [ + sendmail, sendmail-msp, postfix, procmail, exim4, + pickup, showq, mailq, dovecot, imap-login, imap, + mailmng-core, pop3-login, dovecot-lda, pop3 + ] + +# RFC1918 addresses were assigned for private network usage +- list: rfc_1918_addresses + items: ['"10.0.0.0/8"', '"172.16.0.0/12"', '"192.168.0.0/16"'] + +- macro: outbound + condition: > + (((evt.type = connect and evt.dir=<) or + (evt.type in (sendto,sendmsg) and evt.dir=< and + fd.l4proto != tcp and fd.connected=false and fd.name_changed=true)) and + (fd.typechar = 4 or fd.typechar = 6) and + (fd.ip != "0.0.0.0" and fd.net != "127.0.0.0/8" and not fd.snet in (rfc_1918_addresses)) and + (evt.rawres >= 0 or evt.res = EINPROGRESS)) + +# Very similar to inbound/outbound, but combines the tests together +# for efficiency. +- macro: inbound_outbound + condition: > + ((((evt.type in (accept,accept4,listen,connect) and evt.dir=<)) and + (fd.typechar = 4 or fd.typechar = 6)) and + (fd.ip != "0.0.0.0" and fd.net != "127.0.0.0/8") and + (evt.rawres >= 0 or evt.res = EINPROGRESS)) + +- list: bash_config_filenames + items: [.bashrc, .bash_profile, .bash_history, .bash_login, .bash_logout, .inputrc, .profile] + +- list: bash_config_files + items: [/etc/profile, /etc/bashrc] + +# Covers both csh and tcsh +- list: csh_config_filenames + items: [.cshrc, .login, .logout, .history, .tcshrc, .cshdirs] + +- list: csh_config_files + items: [/etc/csh.cshrc, /etc/csh.login] + +- list: zsh_config_filenames + items: [.zshenv, .zprofile, .zshrc, .zlogin, .zlogout] + +- list: shell_config_filenames + items: [bash_config_filenames, csh_config_filenames, zsh_config_filenames] + +- list: shell_config_files + items: [bash_config_files, csh_config_files] + +- list: shell_config_directories + items: [/etc/zsh] + +- macro: user_known_shell_config_modifiers + condition: (never_true) + +- rule: Modify Shell Configuration File + desc: > + Detect attempts to modify shell configuration files, primarily aimed at establishing persistence by automatically inserting + commands into scripts executed by shells. The upstream rule excludes shell processes because they often create unnecessary noise. + However, this might lead to missed detections. To customize the rule for your situation, you can fine-tune it using enhanced profiling. + For example, you might want to only consider interactive shell processes (where proc.tty != 0). + condition: > + open_write + and (fd.filename in (shell_config_filenames) or + fd.name in (shell_config_files) or + fd.directory in (shell_config_directories)) + and not proc.name in (shell_binaries) + and not exe_running_docker_save + and not user_known_shell_config_modifiers + output: A shell configuration file has been modified (file=%fd.name pcmdline=%proc.pcmdline evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: + WARNING + tags: [maturity_incubating, host, container, filesystem, mitre_persistence, T1546.004] + +- macro: user_known_cron_jobs + condition: (never_true) + +- rule: Schedule Cron Jobs + desc: > + Detect scheduled cron jobs; this is a highly generic detection and certainly needs adjustments and profiling in your environment before + operationalization. Simultaneously, exploiting the functionality of cron jobs is among one of the oldest TTPs used by adversaries. + condition: > + ((open_write and fd.name startswith /etc/cron) or + (spawned_process and proc.name = "crontab")) + and not user_known_cron_jobs + output: Cron jobs were scheduled to run (file=%fd.name evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: + NOTICE + tags: [maturity_incubating, host, container, filesystem, mitre_execution, T1053.003] + +# Use this to test whether the event occurred within a container. +# +# When displaying container information in the output field, use +# %container.info, without any leading term (file=%fd.name +# %container.info user=%user.name user_loginuid=%user.loginuid, and not file=%fd.name +# container=%container.info user=%user.name user_loginuid=%user.loginuid). The output will change +# based on the context and whether or not -pk/-pm/-pc was specified on +# the command line. +- macro: container + condition: (container.id != host) + +- macro: container_started + condition: > + ((evt.type = container or + (spawned_process and proc.vpid=1)) and + container.image.repository != incomplete) + +- list: cron_binaries + items: [anacron, cron, crond, crontab] + +# Qualys seems to run a variety of shell subprocesses, at various +# levels. This checks at a few levels without the cost of a full +# proc.aname, which traverses the full parent hierarchy. +- macro: run_by_qualys + condition: > + (proc.pname=qualys-cloud-ag or + proc.aname[2]=qualys-cloud-ag or + proc.aname[3]=qualys-cloud-ag or + proc.aname[4]=qualys-cloud-ag) + +- macro: run_by_sumologic_securefiles + condition: > + ((proc.cmdline="usermod -a -G sumologic_collector" or + proc.cmdline="groupadd sumologic_collector") and + (proc.pname=secureFiles.sh and proc.aexe[2] endswith java)) + +- macro: run_by_yum + condition: ((proc.pname=sh and proc.aname[2]=yum) or + (proc.aname[2]=sh and proc.aname[3]=yum)) + +- macro: run_by_ms_oms + condition: > + (proc.aname[3] startswith omsagent- or + proc.aname[3] startswith scx-) + +- macro: run_by_google_accounts_daemon + condition: > + (proc.aname[1] startswith google_accounts or + proc.aname[2] startswith google_accounts or + proc.aname[3] startswith google_accounts) + +- macro: java_running_sdjagent + condition: (proc.exe endswith java and proc.cmdline contains sdjagent.jar) + +- macro: kubelet_running_loopback + condition: (proc.pname=kubelet and proc.name=loopback) + +- macro: rancher_agent + condition: (proc.name=agent and container.image.repository contains "rancher/agent") + +- macro: rancher_network_manager + condition: (proc.name=rancher-bridge and container.image.repository contains "rancher/network-manager") + +- macro: exe_running_docker_save + condition: > + (proc.name = "exe" + and (proc.cmdline contains "/var/lib/docker" + or proc.cmdline contains "/var/run/docker") + and proc.pname in (dockerd, docker, dockerd-current, docker-current)) + +- macro: user_ssh_directory + condition: (fd.name contains '/.ssh/' and fd.name glob '/home/*/.ssh/*') + +# The rule below is disabled by default as many system management tools +# like ansible, etc can read these files/paths. Enable it using this macro. +- macro: user_known_read_ssh_information_activities + condition: (never_true) + +- rule: Read ssh information + desc: > + This rule identifies attempts to read files within ssh directories using programs that are not related to ssh. It's a simple and + versatile detection method that works well alongside more specific rules focused on sensitive file access. You have a couple of + options for using this rule effectively: you can adjust the specialized rules to cover all the important scenarios and ensure + precedence in rule smatching for those, or you can analyze the combined view of ssh-related file access across various rules on + your downstream computing platform. Just like with other rules, you can narrow down monitoring to specific processes, or you can + limit it to interactive access only. + condition: > + (open_read or open_directory) + and (user_ssh_directory or fd.name startswith /root/.ssh) + and not user_known_read_ssh_information_activities + and not proc.name in (ssh_binaries) + output: ssh-related file/directory read by non-ssh program (file=%fd.name pcmdline=%proc.pcmdline evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: ERROR + tags: [maturity_incubating, host, container, filesystem, mitre_collection, T1005] + +- macro: calico_node + condition: (container.image.repository endswith calico/node and proc.name=calico-node) + +- macro: postgres_running_wal_e + condition: (proc.pname=postgres and (proc.cmdline startswith "sh -c envdir /etc/wal-e.d/env /usr/local/bin/wal-e" or proc.cmdline startswith "sh -c envdir \"/run/etc/wal-e.d/env\" wal-g wal-push")) + +- macro: user_known_db_spawned_processes + condition: (never_true) + +- rule: DB program spawned process + desc: > + A program related to the database server creates an unexpected child process (other than itself). + This is not supposed to happen and often follows SQL injection attacks. This behavioral detection could + indicate potential unauthorized data extraction or tampering with the database. + condition: > + spawned_process + and proc.pname in (db_server_binaries) + and not proc.name in (db_server_binaries) + and not postgres_running_wal_e + and not user_known_db_spawned_processes + output: Database-related program spawned process other than itself (evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) + priority: NOTICE + tags: [maturity_incubating, host, container, process, database, mitre_execution, T1190] + +# This list allows for easy additions to the set of commands allowed +# to change thread namespace without having to copy and override the +# entire change thread namespace rule. +- list: user_known_change_thread_namespace_binaries + items: [crio, multus] + +- macro: user_known_change_thread_namespace_activities + condition: (never_true) + +- list: network_plugin_binaries + items: [aws-cni, azure-vnet] + +- macro: weaveworks_scope + condition: (container.image.repository endswith weaveworks/scope and proc.name=scope) + +- rule: Change thread namespace + desc: > + An attempt to alter the namespace of a process (often performed while creating a container) through the setns syscall. + Conversely, the same syscall setns is triggered when an unauthorized attempt is made to break out from the container + to the host, for example, when using commands like `nsenter --target 1` and similar ones. Recommending to profile your + environment and refine this rule for effective operationalization. + condition: > + evt.type=setns and evt.dir=< + and proc_name_exists + and not (container.id=host and proc.name in (docker_binaries, k8s_binaries, lxd_binaries, nsenter)) + and not proc.name in (sysdigcloud_binaries, sysdig, calico, oci-umount, cilium-cni, network_plugin_binaries) + and not proc.name in (user_known_change_thread_namespace_binaries) + and not proc.name startswith "runc" + and not proc.cmdline startswith "containerd" + and not proc.pname in (sysdigcloud_binaries, hyperkube, kubelet, protokube, dockerd, tini, aws) + and not java_running_sdjagent + and not kubelet_running_loopback + and not rancher_agent + and not rancher_network_manager + and not calico_node + and not weaveworks_scope + and not user_known_change_thread_namespace_activities + output: Namespace change (setns) by unexpected program (evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: NOTICE + tags: [maturity_incubating, host, container, process, mitre_privilege_escalation, T1611] + +- rule: Change namespace privileges via unshare + desc: > + Unprivileged users in containers may not have CAP_SYS_ADMIN or other elevated privileges. However, they can + use the unshare system call with CLONE_NEWNS or CLONE_NEWUSER to create or clone a namespace or user with the + necessary privileges to conduct further attacks. It is best practice to block the unshare system call via + seccomp if it is not needed. Misuse of unshare can be related to misconfigured Kubernetes clusters, for example. + condition: > + evt.type=unshare and evt.dir=< + and container + and not thread.cap_permitted contains CAP_SYS_ADMIN + output: Change namespace privileges via unshare (res=%evt.res evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: NOTICE + tags: [maturity_incubating, container, mitre_privilege_escalation, T1611] + +- macro: allowed_openshift_registry_root + condition: > + (container.image.repository startswith openshift3/ or + container.image.repository startswith registry.redhat.io/openshift3/ or + container.image.repository startswith registry.access.redhat.com/openshift3/) + +# Source: https://docs.openshift.com/enterprise/3.2/install_config/install/disconnected_install.html +- macro: openshift_image + condition: > + (allowed_openshift_registry_root and + (container.image.repository endswith /logging-deployment or + container.image.repository endswith /logging-elasticsearch or + container.image.repository endswith /logging-kibana or + container.image.repository endswith /logging-fluentd or + container.image.repository endswith /logging-auth-proxy or + container.image.repository endswith /metrics-deployer or + container.image.repository endswith /metrics-hawkular-metrics or + container.image.repository endswith /metrics-cassandra or + container.image.repository endswith /metrics-heapster or + container.image.repository endswith /ose-haproxy-router or + container.image.repository endswith /ose-deployer or + container.image.repository endswith /ose-sti-builder or + container.image.repository endswith /ose-docker-builder or + container.image.repository endswith /ose-pod or + container.image.repository endswith /ose-node or + container.image.repository endswith /ose-docker-registry or + container.image.repository endswith /prometheus-node-exporter or + container.image.repository endswith /image-inspector)) + +- list: redhat_io_images_privileged + items: [registry.redhat.io/openshift-logging/fluentd-rhel8, registry.redhat.io/openshift4/ose-csi-node-driver-registrar, registry.redhat.io/openshift4/ose-kubernetes-nmstate-handler-rhel8, registry.redhat.io/openshift4/ose-local-storage-diskmaker] + +- macro: redhat_image + condition: > + (container.image.repository in (redhat_io_images_privileged)) + +# https://docs.aws.amazon.com/eks/latest/userguide/add-ons-images.html +# official AWS EKS registry list. AWS has different ECR repo per region +- macro: allowed_aws_ecr_registry_root_for_eks + condition: > + (container.image.repository startswith "602401143452.dkr.ecr" or + container.image.repository startswith "877085696533.dkr.ecr" or + container.image.repository startswith "800184023465.dkr.ecr" or + container.image.repository startswith "918309763551.dkr.ecr" or + container.image.repository startswith "961992271922.dkr.ecr" or + container.image.repository startswith "590381155156.dkr.ecr" or + container.image.repository startswith "558608220178.dkr.ecr" or + container.image.repository startswith "151742754352.dkr.ecr" or + container.image.repository startswith "013241004608.dkr.ecr") + +- macro: aws_eks_core_images + condition: > + (allowed_aws_ecr_registry_root_for_eks and + (container.image.repository endswith ".amazonaws.com/amazon-k8s-cni" or + container.image.repository endswith ".amazonaws.com/eks/kube-proxy")) + +# These images are allowed both to run with --privileged and to mount +# sensitive paths from the host filesystem. +# +# NOTE: This list is only provided for backwards compatibility with +# older local falco rules files that may have been appending to +# trusted_images. To make customizations, it's better to add images to +# either privileged_images or falco_sensitive_mount_images. +- list: trusted_images + items: [] + +# Add conditions to this macro (probably in a separate file, +# overwriting this macro) to specify additional containers that are +# trusted and therefore allowed to run privileged *and* with sensitive +# mounts. +# +# Like trusted_images, this is deprecated in favor of +# user_privileged_containers and user_sensitive_mount_containers and +# is only provided for backwards compatibility. +# +# In this file, it just takes one of the images in trusted_containers +# and repeats it. +- macro: user_trusted_containers + condition: (never_true) + +- list: sematext_images + items: [docker.io/sematext/sematext-agent-docker, docker.io/sematext/agent, docker.io/sematext/logagent, + registry.access.redhat.com/sematext/sematext-agent-docker, + registry.access.redhat.com/sematext/agent, + registry.access.redhat.com/sematext/logagent] + +# Falco containers +- list: falco_containers + items: + - falcosecurity/falco + - docker.io/falcosecurity/falco + - public.ecr.aws/falcosecurity/falco + +# Falco no driver containers + - falcosecurity/falco-no-driver + - docker.io/falcosecurity/falco-no-driver + - public.ecr.aws/falcosecurity/falco-no-driver + +# These container images are allowed to run with --privileged and full set of capabilities +- list: falco_privileged_images + items: [ + falco_containers, + docker.io/calico/node, + calico/node, + docker.io/cloudnativelabs/kube-router, + docker.io/docker/ucp-agent, + docker.io/mesosphere/mesos-slave, + docker.io/rook/toolbox, + docker.io/sysdig/sysdig, + gcr.io/google_containers/kube-proxy, + gcr.io/google-containers/startup-script, + gcr.io/projectcalico-org/node, + gke.gcr.io/kube-proxy, + gke.gcr.io/gke-metadata-server, + gke.gcr.io/netd-amd64, + gke.gcr.io/watcher-daemonset, + gcr.io/google-containers/prometheus-to-sd, + registry.k8s.io/ip-masq-agent-amd64, + registry.k8s.io/kube-proxy, + registry.k8s.io/prometheus-to-sd, + quay.io/calico/node, + sysdig/sysdig, + sematext_images, + registry.k8s.io/dns/k8s-dns-node-cache, + mcr.microsoft.com/oss/kubernetes/kube-proxy + ] + +- macro: falco_privileged_containers + condition: (openshift_image or + user_trusted_containers or + aws_eks_core_images or + container.image.repository in (trusted_images) or + container.image.repository in (falco_privileged_images) or + container.image.repository startswith istio/proxy_ or + container.image.repository startswith quay.io/sysdig/) + +# Add conditions to this macro (probably in a separate file, +# overwriting this macro) to specify additional containers that are +# allowed to run privileged +# +# In this file, it just takes one of the images in falco_privileged_images +# and repeats it. +- macro: user_privileged_containers + condition: (never_true) + +- rule: Launch Privileged Container + desc: > + Detect the initial process initiation within a privileged container, with exemptions for known and trusted images. + This rule primarily serves as an excellent auditing mechanism since highly privileged containers, when compromised, + can result in significant harm. For instance, if another rule triggers within such a privileged container, it could be + seen as more suspicious, prompting a closer inspection. + condition: > + container_started + and container + and container.privileged=true + and not falco_privileged_containers + and not user_privileged_containers + and not redhat_image + output: Privileged container started (evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: INFO + tags: [maturity_incubating, container, cis, mitre_execution, T1610, PCI_DSS_10.2.5] + +# These capabilities were used in the past to escape from containers +- macro: excessively_capable_container + condition: > + (thread.cap_permitted contains CAP_SYS_ADMIN + or thread.cap_permitted contains CAP_SYS_MODULE + or thread.cap_permitted contains CAP_SYS_RAWIO + or thread.cap_permitted contains CAP_SYS_PTRACE + or thread.cap_permitted contains CAP_SYS_BOOT + or thread.cap_permitted contains CAP_SYSLOG + or thread.cap_permitted contains CAP_DAC_READ_SEARCH + or thread.cap_permitted contains CAP_NET_ADMIN + or thread.cap_permitted contains CAP_BPF) + +- rule: Launch Excessively Capable Container + desc: > + Identify containers that start with a powerful set of capabilities, with exceptions for recognized trusted images. + Similar to the "Launch Privileged Container" rule, this functions as a robust auditing rule. Compromised highly privileged + containers can lead to substantial harm. For instance, if another rule is triggered within such a container, it might + raise suspicion, prompting closer scrutiny. + condition: > + container_started + and container + and excessively_capable_container + and not falco_privileged_containers + and not user_privileged_containers + output: Excessively capable container started (cap_permitted=%thread.cap_permitted evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: INFO + tags: [maturity_incubating, container, cis, mitre_execution, T1610] + +- macro: login_doing_dns_lookup + condition: (proc.name=login and fd.l4proto=udp and fd.sport=53) + +# sockfamily ip is to exclude certain processes (like 'groups') that communicate on unix-domain sockets +# systemd can listen on ports to launch things like sshd on demand +- rule: System procs network activity + desc: > + Detect any unexpected network activity performed by system binaries that typically shouldn't perform network activity, including + coreutils binaries (like sleep, mkdir, who, date, and others) or user management binaries (such as login, systemd, usermod, deluser, + adduser, chpasswd, and others). This serves as a valuable baseline detection for network-related activities. + condition: > + inbound_outbound + and fd.sockfamily = ip + and (system_procs or proc.name in (shell_binaries)) + and not proc.name in (known_system_procs_network_activity_binaries) + and not login_doing_dns_lookup + and not user_expected_system_procs_network_activity_conditions + output: Known system binary sent/received network traffic (connection=%fd.name lport=%fd.lport rport=%fd.rport fd_type=%fd.type fd_proto=fd.l4proto evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: NOTICE + tags: [maturity_incubating, host, network, process, mitre_execution, T1059] + +# This list allows easily whitelisting system proc names that are +# expected to communicate on the network. +- list: known_system_procs_network_activity_binaries + items: [systemd, hostid, id] + +# This macro allows specifying conditions under which a system binary +# is allowed to communicate on the network. For instance, only specific +# proc.cmdline values could be allowed to be more granular in what is +# allowed. +- macro: user_expected_system_procs_network_activity_conditions + condition: (never_true) + +# When filled in, this should look something like: +# (proc.env contains "HTTP_PROXY=http://my.http.proxy.com ") +# The trailing space is intentional so avoid matching on prefixes of +# the actual proxy. +- macro: allowed_ssh_proxy_env + condition: (never_true) + +- list: http_proxy_binaries + items: [curl, wget] + +- macro: http_proxy_procs + condition: (proc.name in (http_proxy_binaries)) + +- rule: Program run with disallowed http proxy env + desc: > + Detect curl or wget usage with HTTP_PROXY environment variable. Attackers can manipulate the HTTP_PROXY variable's + value to redirect application's internal HTTP requests. This could expose sensitive information like authentication + keys and private data. + condition: > + spawned_process + and http_proxy_procs + and proc.env icontains HTTP_PROXY + and not allowed_ssh_proxy_env + output: Curl or wget run with disallowed HTTP_PROXY environment variable (env=%proc.env evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) + priority: NOTICE + tags: [maturity_incubating, host, container, users, mitre_execution, T1204] + +- list: openvpn_udp_ports + items: [1194, 1197, 1198, 8080, 9201] + +- list: l2tp_udp_ports + items: [500, 1701, 4500, 10000] + +- list: statsd_ports + items: [8125] + +- list: ntp_ports + items: [123] + +# Some applications will connect a udp socket to an address only to +# test connectivity. Assuming the udp connect works, they will follow +# up with a tcp connect that actually sends/receives data. +# +# With that in mind, we listed a few commonly seen ports here to avoid +# some false positives. In addition, we make the main rule opt-in, so +# it's disabled by default. +- list: test_connect_ports + items: [0, 9, 80, 3306] + +- list: expected_udp_ports + items: [53, openvpn_udp_ports, l2tp_udp_ports, statsd_ports, ntp_ports, test_connect_ports] + +- macro: expected_udp_traffic + condition: fd.port in (expected_udp_ports) + +- rule: Unexpected UDP Traffic + desc: > + Detecting UDP traffic on ports other than 53 (DNS) or other commonly used ports. Misusing UDP is a known TTP among attackers. + Monitoring unusual network activity is highly valuable but often generates significant noise, as is the case with this detection. + condition: > + inbound_outbound + and fd.l4proto=udp + and not expected_udp_traffic + output: Unexpected UDP Traffic Seen (connection=%fd.name lport=%fd.lport rport=%fd.rport fd_type=%fd.type fd_proto=fd.l4proto evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: NOTICE + tags: [maturity_incubating, host, container, network, mitre_exfiltration, TA0011] + +- macro: somebody_becoming_themselves + condition: ((user.name=nobody and evt.arg.uid=nobody) or + (user.name=www-data and evt.arg.uid=www-data) or + (user.name=_apt and evt.arg.uid=_apt) or + (user.name=postfix and evt.arg.uid=postfix) or + (user.name=pki-agent and evt.arg.uid=pki-agent) or + (user.name=pki-acme and evt.arg.uid=pki-acme) or + (user.name=nfsnobody and evt.arg.uid=nfsnobody) or + (user.name=postgres and evt.arg.uid=postgres)) + +- macro: nrpe_becoming_nagios + condition: (proc.name=nrpe and evt.arg.uid=nagios) + +# In containers, the user name might be for a uid that exists in the +# container but not on the host. (See +# https://github.com/draios/sysdig/issues/954). So in that case, allow +# a setuid. +- macro: known_user_in_container + condition: (container and not user.name in ("","N/A","")) + +# Add conditions to this macro (probably in a separate file, +# overwriting this macro) to allow for specific combinations of +# programs changing users by calling setuid. +# +# In this file, it just takes one of the condition in the base macro +# and repeats it. +- macro: user_known_non_sudo_setuid_conditions + condition: (user.name=root) + +# sshd, mail programs attempt to setuid to root even when running as non-root. Excluding here to avoid meaningless FPs +- rule: Non sudo setuid + desc: > + Detect attempts to change users through the use of setuid, with exceptions for sudo/su. + The users "root" and "nobody" using setuid on themselves are also excluded, as setuid calls in these cases + typically involve reducing privileges. By setting the setuid bit, an attacker could execute code in a + different user's context, potentially with higher privileges. One drawback is the potential for noise, + as many applications legitimately use this approach. + condition: > + evt.type=setuid and evt.dir=> + and (known_user_in_container or not container) + and not (user.name=root or user.uid=0) + and not somebody_becoming_themselves + and not proc.name in (known_setuid_binaries, userexec_binaries, mail_binaries, docker_binaries, + nomachine_binaries) + and not proc.name startswith "runc:" + and not java_running_sdjagent + and not nrpe_becoming_nagios + and not user_known_non_sudo_setuid_conditions + output: Unexpected setuid call by non-sudo, non-root program (arg_uid=%evt.arg.uid evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: NOTICE + tags: [maturity_incubating, host, container, users, mitre_privilege_escalation, T1548.001] + +- macro: user_known_user_management_activities + condition: (never_true) + +- macro: chage_list + condition: (proc.name=chage and (proc.cmdline contains "-l" or proc.cmdline contains "--list")) + +- rule: User mgmt binaries + desc: > + Detect activity by any programs that can manage users, passwords, or permissions (such as login, systemd, usermod, deluser, adduser, + chpasswd, and others). sudo and su are excluded. Activity in containers is also excluded -- some containers create custom users on + top of a base linux distribution at startup. Some innocuous command lines that don't actually change anything are excluded. You might + want to consider applying this rule to container actions as well. + condition: > + spawned_process + and not container + and proc.name in (user_mgmt_binaries) + and not proc.name in (su, sudo, lastlog, nologin, unix_chkpwd) + and not proc.pname in (cron_binaries, systemd, systemd.postins, udev.postinst, run-parts) + and not proc.cmdline startswith "passwd -S" + and not proc.cmdline startswith "useradd -D" + and not proc.cmdline startswith "systemd --version" + and not run_by_qualys + and not run_by_sumologic_securefiles + and not run_by_yum + and not run_by_ms_oms + and not run_by_google_accounts_daemon + and not chage_list + and not user_known_user_management_activities + output: User management binary command run outside of container (gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) + priority: NOTICE + tags: [maturity_incubating, host, users, software_mgmt, mitre_persistence, T1098] + +- list: allowed_dev_files + items: [ + /dev/null, /dev/stdin, /dev/stdout, /dev/stderr, + /dev/random, /dev/urandom, /dev/console, /dev/kmsg + ] + +- macro: user_known_create_files_below_dev_activities + condition: (never_true) + +# (we may need to add additional checks against false positives, see: +# https://bugs.launchpad.net/ubuntu/+source/rkhunter/+bug/86153) +- rule: Create files below dev + desc: > + Detect the creation of files under /dev except for authorized device management programs. This can reveal rootkits hiding + files in /dev. Additionally, consider the "Execution from /dev/shm" rule. The upstream rule already covers some tuning + scenarios that you can further expand upon. + condition: > + (evt.type = creat or (evt.type in (open,openat,openat2))) + and evt.arg.flags contains O_CREAT + and fd.directory = /dev + and not proc.name in (dev_creation_binaries) + and not fd.name in (allowed_dev_files) + and not fd.name startswith /dev/tty + and not user_known_create_files_below_dev_activities + output: File created below /dev by untrusted program (file=%fd.name evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: ERROR + tags: [maturity_incubating, host, filesystem, mitre_persistence, T1543] + +# In a local/user rules file, you could override this macro to +# explicitly enumerate the container images that you want to allow +# access to EC2 metadata. In this main falco rules file, there isn't +# any way to know all the containers that should have access, so any +# container is allowed, by repeating the "container" macro. In the +# overridden macro, the condition would look something like +# (container.image.repository = vendor/container-1 or +# container.image.repository = vendor/container-2 or ...) +- macro: ec2_metadata_containers + condition: (never_true) + +# On EC2 instances, 169.254.169.254 is a special IP used to fetch +# metadata about the instance. It may be desirable to prevent access +# to this IP from containers. +- rule: Contact EC2 Instance Metadata Service From Container + desc: > + Detects attempts to communicate with the EC2 Instance Metadata Service from a container. This detection is narrowly focused + and might not apply to your environment. In addition, it could generate noise and require fine-tuning. + condition: > + outbound + and container + and fd.sip="169.254.169.254" + and not ec2_metadata_containers + output: Outbound connection to EC2 instance metadata service (connection=%fd.name lport=%fd.lport rport=%fd.rport fd_type=%fd.type fd_proto=fd.l4proto evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: NOTICE + tags: [maturity_incubating, network, aws, container, mitre_credential_access, T1552.005] + +# This rule is not enabled by default, since this rule is for cloud environment(GCP, AWS and Azure) only. +# You can filter the container that you want to allow access to metadata by overwriting user_known_metadata_access macro. +- macro: user_known_metadata_access + condition: (k8s.ns.name = "kube-system") + +# On GCP, AWS and Azure, 169.254.169.254 is a special IP used to fetch +# metadata about the instance. The metadata could be used to get credentials by attackers. +- rule: Contact cloud metadata service from container + desc: > + Detects attempts to communicate with the Cloud Instance Metadata Service from a container. This detection is narrowly focused + and might not apply to your environment. In addition, it could generate noise and require fine-tuning. + condition: > + outbound + and container + and fd.sip="169.254.169.254" + and not user_known_metadata_access + enabled: true + output: Outbound connection to cloud instance metadata service (connection=%fd.name lport=%fd.lport rport=%fd.rport fd_type=%fd.type fd_proto=fd.l4proto evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: NOTICE + tags: [maturity_incubating, network, container, mitre_discovery, T1565] + +- list: network_tool_binaries + items: [nc, ncat, netcat, nmap, dig, tcpdump, tshark, ngrep, telnet, mitmproxy, socat, zmap] + +- macro: network_tool_procs + condition: (proc.name in (network_tool_binaries)) + +# In a local/user rules file, create a condition that matches legitimate uses +# of a package management process inside a container. +# +# For example: +# - macro: user_known_package_manager_in_container +# condition: proc.cmdline="dpkg -l" +- macro: user_known_package_manager_in_container + condition: (never_true) + +# Container is supposed to be immutable. Package management should be done in building the image. +- macro: pkg_mgmt_in_kube_proxy + condition: > + (proc.cmdline startswith "update-alternat" + and container.image.repository = "registry.k8s.io/kube-proxy") + +- rule: Launch Package Management Process in Container + desc: > + Detect package management processes executed within containers. An excellent auditing rule to monitor general drifts + in containers. Particularly useful for newer rules like "Drop and execute new binary in container" during incident + response investigations. This helps identify common anti-patterns of ad-hoc debugging. Simultaneously, to maintain + optimal hygiene, it's recommended to prevent container drifts and instead opt for redeploying new containers. + condition: > + spawned_process + and container + and user.name != "_apt" + and package_mgmt_procs + and not package_mgmt_ancestor_procs + and not user_known_package_manager_in_container + and not pkg_mgmt_in_kube_proxy + output: Package management process launched in container (evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) + priority: ERROR + tags: [maturity_incubating, container, process, software_mgmt, mitre_persistence, T1505] + +- macro: user_known_network_tool_activities + condition: (never_true) + +- rule: Launch Suspicious Network Tool in Container + desc: > + Detect network tools (like netcat, nmap, tcpdump, socat, and more) launched within containers without any additional filters. + This serves as a valuable general detection, but it's recommended to invest engineering effort to fine-tune it and prevent a + high volume of legitimate logs. This rule complements the more specific "Netcat Remote Code Execution in Container" rule. + condition: > + spawned_process + and container + and network_tool_procs + and not user_known_network_tool_activities + output: Network tool launched in container (evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) + priority: NOTICE + tags: [maturity_incubating, container, network, process, mitre_execution, T1059] + +- rule: Launch Suspicious Network Tool on Host + desc: > + Detect network tools (like netcat, nmap, tcpdump, socat, and more) launched within containers without any additional filters. + This serves as a valuable general detection, but it's recommended to invest engineering effort to fine-tune it and prevent a + high volume of legitimate logs. The host equivalent of "Launch Suspicious Network Tool in Container.". + condition: > + spawned_process + and not container + and network_tool_procs + and not user_known_network_tool_activities + output: Network tool launched on host (evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags) + priority: NOTICE + tags: [maturity_incubating, host, network, process, mitre_execution, T1059] + +# here `ash_history` will match both `bash_history` and `ash_history` +- macro: modify_shell_history + condition: > + (modify and ( + evt.arg.name endswith "ash_history" or + evt.arg.name endswith "zsh_history" or + evt.arg.name contains "fish_read_history" or + evt.arg.name endswith "fish_history" or + evt.arg.oldpath endswith "ash_history" or + evt.arg.oldpath endswith "zsh_history" or + evt.arg.oldpath contains "fish_read_history" or + evt.arg.oldpath endswith "fish_history" or + evt.arg.path endswith "ash_history" or + evt.arg.path endswith "zsh_history" or + evt.arg.path contains "fish_read_history" or + evt.arg.path endswith "fish_history")) + +# here `ash_history` will match both `bash_history` and `ash_history` +- macro: truncate_shell_history + condition: > + (open_write and ( + fd.name endswith "ash_history" or + fd.name endswith "zsh_history" or + fd.name contains "fish_read_history" or + fd.name endswith "fish_history") and evt.arg.flags contains "O_TRUNC") + +- macro: var_lib_docker_filepath + condition: (evt.arg.name startswith /var/lib/docker or fd.name startswith /var/lib/docker) + +# todo!: the usage of `evt.arg*` filter check in the output should be avoided +# when more than one event type is involved because some event will populate +# the filtercheck and others will always return . It would be better to use +# a more generic filter like `fs.path.*` +- rule: Delete or rename shell history + desc: > + Detect shell history deletion, frequently used by unsophisticated adversaries to eliminate evidence. + Note that it can also trigger when exiting a Terminal shell, such as with `kubectl exec`, which + may introduce some noise. + condition: > + (modify_shell_history or truncate_shell_history) + and not var_lib_docker_filepath + and not proc.name in (docker_binaries) + output: Shell history deleted or renamed (file=%fd.name name=%evt.arg.name path=%evt.arg.path oldpath=%evt.arg.oldpath evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: + WARNING + tags: [maturity_incubating, host, container, process, filesystem, mitre_defense_evasion, T1070] + +- list: user_known_chmod_applications + items: [hyperkube, kubelet, k3s-agent] + +# This macro should be overridden in user rules as needed. This is useful if a given application +# should not be ignored altogether with the user_known_chmod_applications list, but only in +# specific conditions. +- macro: user_known_set_setuid_or_setgid_bit_conditions + condition: (never_true) + +# todo!: the usage of `evt.arg*` filter check in the output should be avoided +# when more than one event type is involved because some event will populate +# the filtercheck and others will always return . In this specific +# rule, 'chmod' doesn't have a `%evt.arg.fd` argument for example so +# we will always return ``. +- rule: Set Setuid or Setgid bit + desc: > + This rule is focused on detecting the use of setuid or setgid bits set via chmod. These bits, when set for an application, + result in the application running with the privileges of the owning user or group. By enabling the setuid or setgid bits, + an attacker could run code in a different user's context, possibly with elevated privileges. However, there's a trade-off + with noise, given that numerous applications legitimately run chmod. This rule is related to the "Non sudo setuid" rule. + condition: > + chmod + and (evt.arg.mode contains "S_ISUID" or evt.arg.mode contains "S_ISGID") + and not proc.name in (user_known_chmod_applications) + and not exe_running_docker_save + and not user_known_set_setuid_or_setgid_bit_conditions + output: Setuid or setgid bit is set via chmod (fd=%evt.arg.fd filename=%evt.arg.filename mode=%evt.arg.mode evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: + NOTICE + tags: [maturity_incubating, host, container, process, users, mitre_privilege_escalation, T1548.001] + +- list: remote_file_copy_binaries + items: [rsync, scp, sftp, dcp] + +- macro: remote_file_copy_procs + condition: (proc.name in (remote_file_copy_binaries)) + +# Users should overwrite this macro to specify conditions under which a +# Custom condition for use of remote file copy tool in container +- macro: user_known_remote_file_copy_activities + condition: (never_true) + +- rule: Launch Remote File Copy Tools in Container + desc: > + Detect remote file copy tools (like rsync, scp, sftp, dcp) launched within a container, potentially indicating data + exfiltration. Suggest refining this rule to accommodate legitimate use cases. + condition: > + spawned_process + and container + and remote_file_copy_procs + and not user_known_remote_file_copy_activities + output: Remote file copy tool launched in container (evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) + priority: NOTICE + tags: [maturity_incubating, container, network, process, mitre_exfiltration, T1020] + +# Namespaces where the rule is enforce +- list: namespace_scope_network_only_subnet + items: [] + +- macro: network_local_subnet + condition: > + (fd.rnet in (rfc_1918_addresses) or + fd.ip = "0.0.0.0" or + fd.net = "127.0.0.0/8") + +# # The rule is disabled by default. +# # How to test: +# # Add 'default' to namespace_scope_network_only_subnet +# # Run: +# kubectl run --generator=run-pod/v1 -n default -i --tty busybox --image=busybox --rm -- wget google.com -O /var/google.html +# # Check logs running +- rule: Network Connection outside Local Subnet + desc: > + Detect network traffic (inbound or outbound) from a container to a destination outside the local subnet. + To operationalize this rule, profile your environment and update the template macro namespace_scope_network_only_subnet. + Customizing network-related rules usually demands substantial engineering effort to ensure their functionality. + condition: > + inbound_outbound + and container + and k8s.ns.name in (namespace_scope_network_only_subnet) + and not network_local_subnet + output: Network connection outside local subnet (fd_rip_name=%fd.rip.name fd_lip_name=%fd.lip.name fd_cip_name=%fd.cip.name fd_sip_name=%fd.sip.name connection=%fd.name lport=%fd.lport rport=%fd.rport fd_type=%fd.type fd_proto=fd.l4proto evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: WARNING + tags: [maturity_incubating, container, network, mitre_discovery, T1046, PCI_DSS_6.4.2] + +- macro: mount_info + condition: (proc.args="" or proc.args intersects ("-V", "-l", "-h")) + +- macro: known_gke_mount_in_privileged_containers + condition: + (k8s.ns.name = kube-system + and container.image.repository = gke.gcr.io/gcp-compute-persistent-disk-csi-driver) + +- macro: known_aks_mount_in_privileged_containers + condition: + ((k8s.ns.name = kube-system and container.image.repository in (mcr.microsoft.com/oss/kubernetes-csi/azuredisk-csi,mcr.microsoft.com/oss/kubernetes-csi/azurefile-csi)) + or (k8s.ns.name = system and container.image.repository = mcr.microsoft.com/oss/kubernetes-csi/secrets-store/driver)) + +- macro: user_known_mount_in_privileged_containers + condition: (never_true) + +- rule: Mount Launched in Privileged Container + desc: > + Detect filesystem mounts (using the mount binary) within a privileged container. Due to the elevated privileges, + this action could be one of the TTPs used in an attempt to escape from a container to the host. This type of action + is often preceded by reconnaissance activities, for which you can also create custom rules. + condition: > + spawned_process + and container + and container.privileged=true + and proc.name=mount + and not mount_info + and not known_gke_mount_in_privileged_containers + and not known_aks_mount_in_privileged_containers + and not user_known_mount_in_privileged_containers + output: Mount was executed inside a privileged container (evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) + priority: WARNING + tags: [maturity_incubating, container, cis, filesystem, mitre_privilege_escalation, T1611] + +- list: ingress_remote_file_copy_binaries + items: [wget] + +- macro: ingress_remote_file_copy_procs + condition: (proc.name in (ingress_remote_file_copy_binaries)) + +# Users should overwrite this macro to specify conditions under which a +# Custom condition for use of ingress remote file copy tool in container +- macro: user_known_ingress_remote_file_copy_activities + condition: (never_true) + +- macro: curl_download + condition: (proc.name = curl and + (proc.cmdline contains " -o " or + proc.cmdline contains " --output " or + proc.cmdline contains " -O " or + proc.cmdline contains " --remote-name ")) + +- rule: Launch Ingress Remote File Copy Tools in Container + desc: > + Detect ingress remote file copy tools (such as curl or wget) launched inside containers. This rule can be + considered a valuable auditing tool, but it has the potential to generate notable noise and requires careful + profiling before full operationalization. + condition: > + spawned_process + and container + and (ingress_remote_file_copy_procs or curl_download) + and not user_known_ingress_remote_file_copy_activities + output: Ingress remote file copy tool launched in container (evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) + priority: NOTICE + tags: [maturity_incubating, container, network, process, mitre_command_and_control, TA0011] + +- list: docker_binaries + items: [docker, dockerd, containerd-shim, "runc:[1:CHILD]", pause, exe, docker-compose, docker-entrypoi, docker-runc-cur, docker-current, dockerd-current] + +- list: known_binaries_to_read_environment_variables_from_proc_files + items: [scsi_id, argoexec] + +- rule: Read environment variable from /proc files + desc: > + An attempt to read process environment variables from /proc files. The consequences are akin to accessing traditional + sensitive files, as sensitive data, including secrets, might be stored in environment variables. Understanding your + environment, such as identifying critical namespaces, and incorporating extra filtering statements to alert exclusively + for those, can enhance the rule's effectiveness. + condition: > + open_read + and container + and (fd.name glob /proc/*/environ) + and not proc.name in (known_binaries_to_read_environment_variables_from_proc_files) + output: Environment variables were retrieved from /proc files (file=%fd.name gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: WARNING + tags: [maturity_incubating, container, filesystem, process, mitre_discovery, T1083] + +# The steps libcontainer performs to set up the root program for a container are: +# - clone + exec self to a program runc:[0:PARENT] +# - clone a program runc:[1:CHILD] which sets up all the namespaces +# - clone a second program runc:[2:INIT] + exec to the root program. +# The parent of runc:[2:INIT] is runc:0:PARENT] +# As soon as 1:CHILD is created, 0:PARENT exits, so there's a race +# where at the time 2:INIT execs the root program, 0:PARENT might have +# already exited, or might still be around. So we handle both. +# We also let runc:[1:CHILD] count as the parent process, which can occur +# when we lose events and lose track of state. +- macro: container_entrypoint + condition: (not proc.pname exists or proc.pname in (runc:[0:PARENT], runc:[1:CHILD], runc, docker-runc, exe, docker-runc-cur, containerd-shim, systemd, crio)) + +- macro: system_level_side_effect_artifacts_kubectl_cp + condition: (fd.name startswith /etc or + fd.name startswith /proc or + fd.name startswith /lib or + fd.name startswith /run or + fd.name startswith /usr or + fd.name="/") + +- rule: Exfiltrating Artifacts via Kubernetes Control Plane + desc: > + Detect the copying of artifacts from a container's file system using the Kubernetes control plane (kubectl cp). + This rule can identify potential exfiltration of application secrets from containers' file systems, potentially + revealing the outcomes of unauthorized access and control plane misuse via stolen identities (such as stolen + credentials like Kubernetes serviceaccount tokens). Can be customized by the adopter to only monitor specific + artifact paths, containers, or namespaces as needed. + condition: > + open_read + and container + and proc.name=tar + and container_entrypoint + and proc.tty=0 + and not system_level_side_effect_artifacts_kubectl_cp + output: Exfiltrating Artifacts via Kubernetes Control Plane (file=%fd.name evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: NOTICE + tags: [maturity_incubating, container, filesystem, mitre_exfiltration, TA0010] + +- rule: Adding ssh keys to authorized_keys + desc: > + After gaining access, attackers can modify the authorized_keys file to maintain persistence on a victim host. + Where authorized_keys files are modified via cloud APIs or command line interfaces, an adversary may achieve + privilege escalation on the target virtual machine if they add a key to a higher-privileged user. + This rules aims at detecting any modification to the authorized_keys file, that is usually located under the .ssh + directory in any user's home directory. This rule complements the more generic auditing rule "Read ssh information" + by specifically detecting the writing of new, potentially attacker-provided keys. + condition: > + open_write + and (user_ssh_directory or fd.name startswith /root/.ssh) + and fd.name endswith authorized_keys + and not proc.name in (ssh_binaries) + output: Adding ssh keys to authorized_keys (file=%fd.name evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty) + priority: WARNING + tags: [maturity_incubating, host, filesystem, mitre_persistence, T1098.004] + +# possible use of CVE-2023-4911 +- macro: glibc_tunables_env + condition: (proc.env icontains GLIBC_TUNABLES) + +- rule: Potential Local Privilege Escalation via Environment Variables Misuse + desc: > + Process run with suspect environment variable that could be attempting privilege escalation. One use case is + detecting the use of the GLIBC_TUNABLES environment variable, which could be used for privilege escalation + on systems running vulnerable glibc versions. Only known and carefully profiled processes that legitimately + exhibit this behavior should be excluded from this rule. This rule is expected to trigger on every attempt, + even failed ones. + condition: > + spawned_process + and glibc_tunables_env + enabled: true + output: Process run with suspect environment variable which could be attempting privilege escalation (env=%proc.env evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) + priority: NOTICE + tags: [maturity_incubating, host, container, users, mitre_privilege_escalation, TA0004] + +- rule: Backdoored library loaded into SSHD (CVE-2024-3094) + desc: This rule detects possible CVE-2024-3094 exploitation when the SSH daemon process loads a vulnerable version of the liblzma library. An attacker could exploit this to interfere with authentication in sshd via systemd, potentially compromising sensitive data or escalating their privileges. + condition: > + open_read and + proc.name=sshd and + (fd.name contains "liblzma.so.5.6.0" or fd.name contains "liblzma.so.5.6.1") + output: SSHD loaded a backdoored version of liblzma library %fd.name with parent %proc.pname and cmdline %proc.cmdline (process=%proc.name parent=%proc.pname file=%fd.name evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid proc_exepath=%proc.exepath command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) + priority: WARNING + tags: [maturity_incubating, host, container, mitre_initial_access, T1556] + +- list: bpf_profiled_binaries + items: [falco, bpftool, systemd] + +- macro: bpf_profiled_procs + condition: (proc.name in (bpf_profiled_binaries)) + +- rule: BPF Program Not Profiled + desc: > + BPF is a kernel technology that can be misused for malicious purposes, like "Linux Kernel Module Injection". This + rule should be considered an auditing rule to notify you of any unprofiled BPF tools running in your environment. + However, it requires customization after profiling your environment. BPF-powered agents make bpf syscalls all the + time, so this rule only sends logs for BPF_PROG_LOAD calls (bpf cmd=BPF_PROG_LOAD) in the enter event. If you also want to log + whether the syscall failed or succeeded, remove the direction filter and add the evt.arg.res_or_fd output field. + condition: > + evt.type=bpf and evt.dir=> + and evt.arg.cmd=BPF_PROG_LOAD + and not bpf_profiled_procs + output: BPF Program Not Profiled (bpf_cmd=%evt.arg.cmd evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: NOTICE + tags: [maturity_incubating, host, container, mitre_persistence, TA0003] diff --git a/rules/falco-sandbox_rules.yaml b/rules/falco-sandbox_rules.yaml new file mode 100644 index 000000000..c3a17da9b --- /dev/null +++ b/rules/falco-sandbox_rules.yaml @@ -0,0 +1,1753 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (C) 2023 The Falco Authors. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Information about rules tags and fields can be found here: https://falco.org/docs/rules/#tags-for-current-falco-ruleset +# The initial item in the `tags` fields reflects the maturity level of the rules introduced upon the proposal https://github.com/falcosecurity/rules/blob/main/proposals/20230605-rules-adoption-management-maturity-framework.md +# `tags` fields also include information about the type of workload inspection (host and/or container), and Mitre Attack killchain phases and Mitre TTP code(s) +# Mitre Attack References: +# [1] https://attack.mitre.org/tactics/enterprise/ +# [2] https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json + +# Starting with version 8, the Falco engine supports exceptions. +# However the Falco rules file does not use them by default. +- required_engine_version: 0.35.0 + +# Currently disabled as read/write are ignored syscalls. The nearly +# similar open_write/open_read check for files being opened for +# reading/writing. +# - macro: write +# condition: (syscall.type=write and fd.type in (file, directory)) +# - macro: read +# condition: (syscall.type=read and evt.dir=> and fd.type in (file, directory)) + +- macro: open_write + condition: (evt.type in (open,openat,openat2) and evt.is_open_write=true and fd.typechar='f' and fd.num>=0) + +- macro: open_read + condition: (evt.type in (open,openat,openat2) and evt.is_open_read=true and fd.typechar='f' and fd.num>=0) + +# This macro `never_true` is used as placeholder for tuning negative logical sub-expressions, for example +# - macro: allowed_ssh_hosts +# condition: (never_true) +# can be used in a rules' expression with double negation `and not allowed_ssh_hosts` which effectively evaluates +# to true and does nothing, the perfect empty template for `logical` cases as opposed to list templates. +# When tuning the rule you can override the macro with something useful, e.g. +# - macro: allowed_ssh_hosts +# condition: (evt.hostname contains xyz) +- macro: never_true + condition: (evt.num=0) + +# This macro `always_true` is the flip side of the macro `never_true` and currently is commented out as +# it is not used. You can use it as placeholder for a positive logical sub-expression tuning template +# macro, e.g. `and custom_procs`, where +# - macro: custom_procs +# condition: (always_true) +# later you can customize, override the macros to something like +# - macro: custom_procs +# condition: (proc.name in (custom1, custom2, custom3)) +# - macro: always_true +# condition: (evt.num>=0) + +# In some cases, such as dropped system call events, information about +# the process name may be missing. For some rules that really depend +# on the identity of the process performing an action such as opening +# a file, etc., we require that the process name be known. +- macro: proc_name_exists + condition: (not proc.name in ("","N/A")) + +- macro: rename + condition: (evt.type in (rename, renameat, renameat2)) + +- macro: mkdir + condition: (evt.type in (mkdir, mkdirat)) + +- macro: remove + condition: (evt.type in (rmdir, unlink, unlinkat)) + +- macro: modify + condition: (rename or remove) + +- macro: spawned_process + condition: (evt.type in (execve, execveat) and evt.dir=<) + +- macro: chmod + condition: (evt.type in (chmod, fchmod, fchmodat) and evt.dir=<) + +# File categories +- macro: bin_dir + condition: (fd.directory in (/bin, /sbin, /usr/bin, /usr/sbin)) + +- macro: bin_dir_mkdir + condition: > + (evt.arg.path startswith /bin/ or + evt.arg.path startswith /sbin/ or + evt.arg.path startswith /usr/bin/ or + evt.arg.path startswith /usr/sbin/) + +- macro: bin_dir_rename + condition: > + (evt.arg.path startswith /bin/ or + evt.arg.path startswith /sbin/ or + evt.arg.path startswith /usr/bin/ or + evt.arg.path startswith /usr/sbin/ or + evt.arg.name startswith /bin/ or + evt.arg.name startswith /sbin/ or + evt.arg.name startswith /usr/bin/ or + evt.arg.name startswith /usr/sbin/ or + evt.arg.oldpath startswith /bin/ or + evt.arg.oldpath startswith /sbin/ or + evt.arg.oldpath startswith /usr/bin/ or + evt.arg.oldpath startswith /usr/sbin/ or + evt.arg.newpath startswith /bin/ or + evt.arg.newpath startswith /sbin/ or + evt.arg.newpath startswith /usr/bin/ or + evt.arg.newpath startswith /usr/sbin/) + +- macro: etc_dir + condition: (fd.name startswith /etc/) + +# This detects writes immediately below / or any write anywhere below /root +- macro: root_dir + condition: (fd.directory=/ or fd.name startswith /root/) + +- list: shell_binaries + items: [ash, bash, csh, ksh, sh, tcsh, zsh, dash] + +- list: shell_mgmt_binaries + items: [add-shell, remove-shell] + +# dpkg -L passwd | grep bin | xargs ls -ld | grep -v '^d' | awk '{print $9}' | xargs -L 1 basename | tr "\\n" "," +- list: passwd_binaries + items: [ + shadowconfig, grpck, pwunconv, grpconv, pwck, + groupmod, vipw, pwconv, useradd, newusers, cppw, chpasswd, usermod, + groupadd, groupdel, grpunconv, chgpasswd, userdel, chage, chsh, + gpasswd, chfn, expiry, passwd, vigr, cpgr, adduser, addgroup, deluser, delgroup + ] + +# repoquery -l shadow-utils | grep bin | xargs ls -ld | grep -v '^d' | +# awk '{print $9}' | xargs -L 1 basename | tr "\\n" "," +- list: shadowutils_binaries + items: [ + chage, gpasswd, lastlog, newgrp, sg, adduser, deluser, chpasswd, + groupadd, groupdel, addgroup, delgroup, groupmems, groupmod, grpck, grpconv, grpunconv, + newusers, pwck, pwconv, pwunconv, useradd, userdel, usermod, vigr, vipw, unix_chkpwd + ] + +- list: sysdigcloud_binaries + items: [setup-backend, dragent, sdchecks] + +- list: interpreted_binaries + items: [lua, node, perl, perl5, perl6, php, python, python2, python3, ruby, tcl] + +- macro: interpreted_procs + condition: > + (proc.name in (interpreted_binaries)) + +# The explicit quotes are needed to avoid the - characters being +# interpreted by the filter expression. +- list: rpm_binaries + items: [dnf, dnf-automatic, rpm, rpmkey, yum, '"75-system-updat"', rhsmcertd-worke, rhsmcertd, subscription-ma, + repoquery, rpmkeys, rpmq, yum-cron, yum-config-mana, yum-debug-dump, + abrt-action-sav, rpmdb_stat, microdnf, rhn_check, yumdb] + +- list: openscap_rpm_binaries + items: [probe_rpminfo, probe_rpmverify, probe_rpmverifyfile, probe_rpmverifypackage] + +- macro: rpm_procs + condition: (proc.name in (rpm_binaries, openscap_rpm_binaries) or proc.name in (salt-call, salt-minion)) + +- list: deb_binaries + items: [dpkg, dpkg-preconfigu, dpkg-reconfigur, dpkg-divert, apt, apt-get, aptitude, + frontend, preinst, add-apt-reposit, apt-auto-remova, apt-key, + apt-listchanges, unattended-upgr, apt-add-reposit, apt-cache, apt.systemd.dai + ] +- list: python_package_managers + items: [pip, pip3, conda] + +# The truncated dpkg-preconfigu is intentional, process names are +# truncated at the falcosecurity-libs level. +- list: package_mgmt_binaries + items: [rpm_binaries, deb_binaries, update-alternat, gem, npm, python_package_managers, sane-utils.post, alternatives, chef-client, apk, snapd] + +- macro: package_mgmt_procs + condition: (proc.name in (package_mgmt_binaries)) + +- macro: package_mgmt_ancestor_procs + condition: (proc.pname in (package_mgmt_binaries) or + proc.aname[2] in (package_mgmt_binaries) or + proc.aname[3] in (package_mgmt_binaries) or + proc.aname[4] in (package_mgmt_binaries)) + +- macro: coreos_write_ssh_dir + condition: (proc.name=update-ssh-keys and fd.name startswith /home/core/.ssh) + +- list: ssl_mgmt_binaries + items: [ca-certificates] + +- list: dhcp_binaries + items: [dhclient, dhclient-script, 11-dhclient] + +- list: dev_creation_binaries + items: [blkid, rename_device, update_engine, sgdisk] + +- list: nomachine_binaries + items: [nxexec, nxnode.bin, nxserver.bin, nxclient.bin] + +- list: mail_config_binaries + items: [ + update_conf, parse_mc, makemap_hash, newaliases, update_mk, update_tlsm4, + update_db, update_mc, ssmtp.postinst, mailq, postalias, postfix.config., + postfix.config, postfix-script, postconf + ] + +# Network +- macro: inbound + condition: > + (((evt.type in (accept,accept4,listen) and evt.dir=<) or + (evt.type in (recvfrom,recvmsg) and evt.dir=< and + fd.l4proto != tcp and fd.connected=false and fd.name_changed=true)) and + (fd.typechar = 4 or fd.typechar = 6) and + (fd.ip != "0.0.0.0" and fd.net != "127.0.0.0/8") and + (evt.rawres >= 0 or evt.res = EINPROGRESS)) + +# RFC1918 addresses were assigned for private network usage +- list: rfc_1918_addresses + items: ['"10.0.0.0/8"', '"172.16.0.0/12"', '"192.168.0.0/16"'] + +- macro: outbound + condition: > + (((evt.type = connect and evt.dir=<) or + (evt.type in (sendto,sendmsg) and evt.dir=< and + fd.l4proto != tcp and fd.connected=false and fd.name_changed=true)) and + (fd.typechar = 4 or fd.typechar = 6) and + (fd.ip != "0.0.0.0" and fd.net != "127.0.0.0/8" and not fd.snet in (rfc_1918_addresses)) and + (evt.rawres >= 0 or evt.res = EINPROGRESS)) + +# Very similar to inbound/outbound, but combines the tests together +# for efficiency. +- macro: inbound_outbound + condition: > + ((((evt.type in (accept,accept4,listen,connect) and evt.dir=<)) and + (fd.typechar = 4 or fd.typechar = 6)) and + (fd.ip != "0.0.0.0" and fd.net != "127.0.0.0/8") and + (evt.rawres >= 0 or evt.res = EINPROGRESS)) + +- list: allowed_inbound_source_ipaddrs + items: ['"127.0.0.1"'] + +- list: allowed_inbound_source_networks + items: ['"127.0.0.1/8"', '"10.0.0.0/8"'] + +- list: allowed_inbound_source_domains + items: [google.com] + +- rule: Unexpected inbound connection source + desc: > + Detect any inbound connection from a source outside of an allowed set of ips, networks, or domain names. + This rule absolutely requires profiling your environment beforehand. Network-based rules are extremely crucial + in any security program, as they can often provide the only definitive evidence. However, effectively operationalizing + them can be challenging due to the potential for noise. + condition: > + inbound + and not ((fd.cip in (allowed_inbound_source_ipaddrs)) or + (fd.cnet in (allowed_inbound_source_networks)) or + (fd.cip.name in (allowed_inbound_source_domains))) + enabled: false + output: Disallowed inbound connection source (connection=%fd.name lport=%fd.lport rport=%fd.rport fd_type=%fd.type fd_proto=fd.l4proto evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: NOTICE + tags: [maturity_sandbox, host, container, network, mitre_command_and_control, TA0011] + +- list: bash_config_filenames + items: [.bashrc, .bash_profile, .bash_history, .bash_login, .bash_logout, .inputrc, .profile] + +- list: bash_config_files + items: [/etc/profile, /etc/bashrc] + +# Covers both csh and tcsh +- list: csh_config_filenames + items: [.cshrc, .login, .logout, .history, .tcshrc, .cshdirs] + +- list: csh_config_files + items: [/etc/csh.cshrc, /etc/csh.login] + +- list: zsh_config_filenames + items: [.zshenv, .zprofile, .zshrc, .zlogin, .zlogout] + +- list: shell_config_filenames + items: [bash_config_filenames, csh_config_filenames, zsh_config_filenames] + +- list: shell_config_files + items: [bash_config_files, csh_config_files] + +- list: shell_config_directories + items: [/etc/zsh] + +# This rule is not enabled by default, as there are many legitimate +# readers of shell config files. +- rule: Read Shell Configuration File + desc: > + This rule detects attempts made by non-shell programs to read shell configuration files. It offers additional generic auditing. + It serves as a baseline detection alert for unusual shell configuration file accesses. The rule "Modify Shell Configuration File" + might be more relevant and adequate for your specific cases. + condition: > + open_read + and (fd.filename in (shell_config_filenames) or + fd.name in (shell_config_files) or + fd.directory in (shell_config_directories)) + and not proc.name in (shell_binaries) + enabled: false + output: A shell configuration file was read by a non-shell program (file=%fd.name evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: + WARNING + tags: [maturity_sandbox, host, container, filesystem, mitre_discovery, T1546.004] + +# Use this to test whether the event occurred within a container. +# When displaying container information in the output field, use +# %container.info, without any leading term (file=%fd.name +# %container.info user=%user.name user_loginuid=%user.loginuid, and not file=%fd.name +# container=%container.info user=%user.name user_loginuid=%user.loginuid). The output will change +# based on the context and whether or not -pk/-pm/-pc was specified on +# the command line. +- macro: container + condition: (container.id != host) + +- macro: container_started + condition: > + ((evt.type = container or + (spawned_process and proc.vpid=1)) and + container.image.repository != incomplete) + +# Possible scripts run by sshkit +- list: sshkit_script_binaries + items: [10_etc_sudoers., 10_passwd_group] + +- list: plesk_binaries + items: [sw-engine, sw-engine-fpm, sw-engine-kv, filemng, f2bmng] + +- macro: httpd_writing_ssl_conf + condition: > + (proc.pname=run-httpd and + (proc.cmdline startswith "sed -ri" or proc.cmdline startswith "sed -i") and + (fd.name startswith /etc/httpd/conf.d/ or fd.name startswith /etc/httpd/conf)) + +- macro: userhelper_writing_etc_security + condition: (proc.name=userhelper and fd.name startswith /etc/security) + +- macro: ansible_running_python + condition: (proc.name in (python, pypy, python3) and proc.cmdline contains ansible) + +- macro: python_running_chef + condition: (proc.name=python and (proc.cmdline contains yum-dump.py or proc.cmdline="python /usr/bin/chef-monitor.py")) + +- macro: python_running_denyhosts + condition: > + (proc.name=python and + (proc.cmdline contains /usr/sbin/denyhosts or + proc.cmdline contains /usr/local/bin/denyhosts.py)) + +- macro: run_by_chef + condition: (proc.aname[2]=chef_command_wr or proc.aname[3]=chef_command_wr or + proc.aname[2]=chef-client or proc.aname[3]=chef-client or + proc.name=chef-client) + +- macro: run_by_adclient + condition: (proc.aname[2]=adclient or proc.aname[3]=adclient or proc.aname[4]=adclient) + +- macro: run_by_centrify + condition: (proc.aname[2]=centrify or proc.aname[3]=centrify or proc.aname[4]=centrify) + +- macro: parent_supervise_running_multilog + condition: (proc.name=multilog and proc.pname=supervise) + +- macro: supervise_writing_status + condition: (proc.name in (supervise,svc) and fd.name startswith "/etc/sb/") + +- macro: pki_realm_writing_realms + condition: (proc.cmdline startswith "bash /usr/local/lib/pki/pki-realm" and fd.name startswith /etc/pki/realms) + +- macro: htpasswd_writing_passwd + condition: (proc.name=htpasswd and fd.name=/etc/nginx/.htpasswd) + +- macro: lvprogs_writing_conf + condition: > + (proc.name in (dmeventd,lvcreate,pvscan,lvs) and + (fd.name startswith /etc/lvm/archive or + fd.name startswith /etc/lvm/backup or + fd.name startswith /etc/lvm/cache)) + +- macro: ovsdb_writing_openvswitch + condition: (proc.name=ovsdb-server and fd.directory=/etc/openvswitch) + +- macro: parent_ucf_writing_conf + condition: (proc.pname=ucf and proc.aname[2]=frontend) + +- macro: consul_template_writing_conf + condition: > + ((proc.name=consul-template and fd.name startswith /etc/haproxy) or + (proc.name=reload.sh and proc.aname[2]=consul-template and fd.name startswith /etc/ssl)) + +- macro: countly_writing_nginx_conf + condition: (proc.cmdline startswith "nodejs /opt/countly/bin" and fd.name startswith /etc/nginx) + +- list: ms_oms_binaries + items: [omi.postinst, omsconfig.posti, scx.postinst, omsadmin.sh, omiagent] + +- macro: ms_oms_writing_conf + condition: > + ((proc.name in (omiagent,omsagent,in_heartbeat_r*,omsadmin.sh,PerformInventor,dsc_host) + or proc.pname in (ms_oms_binaries) + or proc.aname[2] in (ms_oms_binaries)) + and (fd.name startswith /etc/opt/omi or fd.name startswith /etc/opt/microsoft/omsagent)) + +- macro: ms_scx_writing_conf + condition: (proc.name in (GetLinuxOS.sh) and fd.name startswith /etc/opt/microsoft/scx) + +- macro: azure_scripts_writing_conf + condition: (proc.pname startswith "bash /var/lib/waagent/" and fd.name startswith /etc/azure) + +- macro: azure_networkwatcher_writing_conf + condition: (proc.name in (NetworkWatcherA) and fd.name=/etc/init.d/AzureNetworkWatcherAgent) + +- macro: couchdb_writing_conf + condition: (proc.name=beam.smp and proc.cmdline contains couchdb and fd.name startswith /etc/couchdb) + +- macro: update_texmf_writing_conf + condition: (proc.name=update-texmf and fd.name startswith /etc/texmf) + +- macro: slapadd_writing_conf + condition: (proc.name=slapadd and fd.name startswith /etc/ldap) + +- macro: openldap_writing_conf + condition: (proc.pname=run-openldap.sh and fd.name startswith /etc/openldap) + +- macro: ucpagent_writing_conf + condition: (proc.name=apiserver and container.image.repository=docker/ucp-agent and fd.name=/etc/authorization_config.cfg) + +- macro: iscsi_writing_conf + condition: (proc.name=iscsiadm and fd.name startswith /etc/iscsi) + +- macro: istio_writing_conf + condition: (proc.name=pilot-agent and fd.name startswith /etc/istio) + +- macro: symantec_writing_conf + condition: > + ((proc.name=symcfgd and fd.name startswith /etc/symantec) or + (proc.name=navdefutil and fd.name=/etc/symc-defutils.conf)) + +- macro: liveupdate_writing_conf + condition: (proc.cmdline startswith "java LiveUpdate" and fd.name in (/etc/liveupdate.conf, /etc/Product.Catalog.JavaLiveUpdate)) + +- macro: sosreport_writing_files + condition: > + (proc.name=urlgrabber-ext- and proc.aname[3]=sosreport and + (fd.name startswith /etc/pkt/nssdb or fd.name startswith /etc/pki/nssdb)) + +- macro: pkgmgmt_progs_writing_pki + condition: > + (proc.name=urlgrabber-ext- and proc.pname in (yum, yum-cron, repoquery) and + (fd.name startswith /etc/pkt/nssdb or fd.name startswith /etc/pki/nssdb)) + +- macro: update_ca_trust_writing_pki + condition: (proc.pname=update-ca-trust and proc.name=trust and fd.name startswith /etc/pki) + +- macro: brandbot_writing_os_release + condition: (proc.name=brandbot and fd.name=/etc/os-release) + +- macro: selinux_writing_conf + condition: (proc.name in (semodule,genhomedircon,sefcontext_comp) and fd.name startswith /etc/selinux) + +- list: veritas_binaries + items: [vxconfigd, sfcache, vxclustadm, vxdctl, vxprint, vxdmpadm, vxdisk, vxdg, vxassist, vxtune] + +- macro: veritas_driver_script + condition: (proc.cmdline startswith "perl /opt/VRTSsfmh/bin/mh_driver.pl") + +- macro: veritas_progs + condition: (proc.name in (veritas_binaries) or veritas_driver_script) + +- macro: veritas_writing_config + condition: (veritas_progs and (fd.name startswith /etc/vx or fd.name startswith /etc/opt/VRTS or fd.name startswith /etc/vom)) + +- macro: nginx_writing_conf + condition: (proc.name in (nginx,nginx-ingress-c,nginx-ingress) and (fd.name startswith /etc/nginx or fd.name startswith /etc/ingress-controller)) + +- macro: nginx_writing_certs + condition: > + (((proc.name=openssl and proc.pname=nginx-launch.sh) or proc.name=nginx-launch.sh) and fd.name startswith /etc/nginx/certs) + +- macro: chef_client_writing_conf + condition: (proc.pcmdline startswith "chef-client /opt/gitlab" and fd.name startswith /etc/gitlab) + +- macro: centrify_writing_krb + condition: (proc.name in (adjoin,addns) and fd.name startswith /etc/krb5) + +- macro: sssd_writing_krb + condition: (proc.name=adcli and proc.aname[2]=sssd and fd.name startswith /etc/krb5) + +- macro: cockpit_writing_conf + condition: > + ((proc.pname=cockpit-kube-la or proc.aname[2]=cockpit-kube-la) + and fd.name startswith /etc/cockpit) + +- macro: ipsec_writing_conf + condition: (proc.name=start-ipsec.sh and fd.directory=/etc/ipsec) + +- macro: exe_running_docker_save + condition: > + (proc.name = "exe" + and (proc.cmdline contains "/var/lib/docker" + or proc.cmdline contains "/var/run/docker") + and proc.pname in (dockerd, docker, dockerd-current, docker-current)) + +# Ideally we'd have a length check here as well but +# filterchecks don't have operators like len() +- macro: sed_temporary_file + condition: (proc.name=sed and fd.name startswith "/etc/sed") + +- macro: python_running_get_pip + condition: (proc.cmdline startswith "python get-pip.py") + +- macro: python_running_ms_oms + condition: (proc.cmdline startswith "python /var/lib/waagent/") + +- macro: gugent_writing_guestagent_log + condition: (proc.name=gugent and fd.name=GuestAgent.log) + +- macro: dse_writing_tmp + condition: (proc.name=dse-entrypoint and fd.name=/root/tmp__) + +- macro: zap_writing_state + condition: (proc.exe endswith java and proc.cmdline contains "jar /zap" and fd.name startswith /root/.ZAP) + +- macro: airflow_writing_state + condition: (proc.name=airflow and fd.name startswith /root/airflow) + +- macro: rpm_writing_root_rpmdb + condition: (proc.name=rpm and fd.directory=/root/.rpmdb) + +- macro: maven_writing_groovy + condition: (proc.exe endswith java and proc.cmdline contains "classpath /usr/local/apache-maven" and fd.name startswith /root/.groovy) + +- macro: chef_writing_conf + condition: (proc.name=chef-client and fd.name startswith /root/.chef) + +- macro: kubectl_writing_state + condition: (proc.name in (kubectl,oc) and fd.name startswith /root/.kube) + +- macro: java_running_cassandra + condition: (proc.exe endswith java and proc.cmdline contains "cassandra.jar") + +- macro: cassandra_writing_state + condition: (java_running_cassandra and fd.directory=/root/.cassandra) + +# Istio +- macro: galley_writing_state + condition: (proc.name=galley and fd.name in (known_istio_files)) + +- list: known_istio_files + items: [/healthready, /healthliveness] + +- macro: calico_writing_state + condition: (proc.name=kube-controller and fd.name startswith /status.json and k8s.pod.name startswith calico) + +- macro: calico_writing_envvars + condition: (proc.name=start_runit and fd.name startswith "/etc/envvars" and container.image.repository endswith "calico/node") + +- list: repository_files + items: [sources.list] + +- list: repository_directories + items: [/etc/apt/sources.list.d, /etc/yum.repos.d, /etc/apt] + +- macro: access_repositories + condition: (fd.directory in (repository_directories) or + (fd.name pmatch (repository_directories) and + fd.filename in (repository_files))) + +- macro: modify_repositories + condition: (evt.arg.newpath pmatch (repository_directories)) + +- macro: user_known_update_package_registry + condition: (never_true) + +# todo!: the usage of `evt.arg*` filter check in the output should be avoided +# when more than one event type is involved because some event will populate +# the filtercheck and others will always return . In this specific +# rule, 'open_write' events don't have a `%evt.arg.newpath` argument for example so +# we will always return ``. +- rule: Update Package Repository + desc: > + This rule generically detects updates to package repositories and can be seen as an auditing measure. + Recommend evaluating its relevance for your specific environment. + condition: > + ((open_write and access_repositories) or (modify and modify_repositories)) + and not package_mgmt_procs + and not package_mgmt_ancestor_procs + and not exe_running_docker_save + and not user_known_update_package_registry + output: Repository files get updated (newpath=%evt.arg.newpath file=%fd.name pcmdline=%proc.pcmdline evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: + NOTICE + tags: [maturity_sandbox, host, container, filesystem, mitre_execution, T1072] + +# Users should overwrite this macro to specify conditions under which a +# write under the binary dir is ignored. For example, it may be okay to +# install a binary in the context of a ci/cd build. +- macro: user_known_write_below_binary_dir_activities + condition: (never_true) + +- rule: Write below binary dir + desc: > + Trying to write to any file below specific binary directories can serve as an auditing rule to track general system changes. + Such rules can be noisy and challenging to interpret, particularly if your system frequently undergoes updates. However, careful + profiling of your environment can transform this rule into an effective rule for detecting unusual behavior associated with system + changes, including compliance-related cases. + condition: > + open_write and evt.dir=< + and bin_dir + and not package_mgmt_procs + and not exe_running_docker_save + and not python_running_get_pip + and not python_running_ms_oms + and not user_known_write_below_binary_dir_activities + output: File below a known binary directory opened for writing (file=%fd.name pcmdline=%proc.pcmdline gparent=%proc.aname[2] evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: ERROR + tags: [maturity_sandbox, host, container, filesystem, mitre_persistence, T1543] + +# If you'd like to generally monitor a wider set of directories on top +# of the ones covered by the rule Write below binary dir, you can use +# the following rule and lists. +- list: monitored_directories + items: [/boot, /lib, /lib64, /usr/lib, /usr/local/lib, /usr/local/sbin, /usr/local/bin, /root/.ssh] + +- macro: user_ssh_directory + condition: (fd.name contains '/.ssh/' and fd.name glob '/home/*/.ssh/*') + +# google_accounts_(daemon) +- macro: google_accounts_daemon_writing_ssh + condition: (proc.name=google_accounts and user_ssh_directory) + +- macro: cloud_init_writing_ssh + condition: (proc.name=cloud-init and user_ssh_directory) + +- macro: mkinitramfs_writing_boot + condition: (proc.pname in (mkinitramfs, update-initramf) and fd.directory=/boot) + +- macro: monitored_dir + condition: > + (fd.directory in (monitored_directories) + or user_ssh_directory) + and not mkinitramfs_writing_boot + +# Add conditions to this macro (probably in a separate file, +# overwriting this macro) to allow for specific combinations of +# programs writing below monitored directories. +# +# Its default value is an expression that always is false, which +# becomes true when the "not ..." in the rule is applied. +- macro: user_known_write_monitored_dir_conditions + condition: (never_true) + +- rule: Write below monitored dir + desc: > + Trying to write to any file below a set of monitored directories can serve as an auditing rule to track general system changes. + Such rules can be noisy and challenging to interpret, particularly if your system frequently undergoes updates. However, careful + profiling of your environment can transform this rule into an effective rule for detecting unusual behavior associated with system + changes, including compliance-related cases. + condition: > + open_write and evt.dir=< + and monitored_dir + and not package_mgmt_procs + and not coreos_write_ssh_dir + and not exe_running_docker_save + and not python_running_get_pip + and not python_running_ms_oms + and not google_accounts_daemon_writing_ssh + and not cloud_init_writing_ssh + and not user_known_write_monitored_dir_conditions + output: File below a monitored directory opened for writing (file=%fd.name pcmdline=%proc.pcmdline gparent=%proc.aname[2] evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: ERROR + tags: [maturity_sandbox, host, container, filesystem, mitre_persistence, T1543] + +- list: safe_etc_dirs + items: [/etc/cassandra, /etc/ssl/certs/java, /etc/logstash, /etc/nginx/conf.d, /etc/container_environment, /etc/hrmconfig, /etc/fluent/configs.d. /etc/alertmanager] + +- macro: fluentd_writing_conf_files + condition: (proc.name=start-fluentd and fd.name in (/etc/fluent/fluent.conf, /etc/td-agent/td-agent.conf)) + +- macro: qualys_writing_conf_files + condition: (proc.name=qualys-cloud-ag and fd.name=/etc/qualys/cloud-agent/qagent-log.conf) + +- macro: git_writing_nssdb + condition: (proc.name=git-remote-http and fd.directory=/etc/pki/nssdb) + +- macro: plesk_writing_keys + condition: (proc.name in (plesk_binaries) and fd.name startswith /etc/sw/keys) + +- macro: plesk_install_writing_apache_conf + condition: (proc.cmdline startswith "bash -hB /usr/lib/plesk-9.0/services/webserver.apache configure" + and fd.name="/etc/apache2/apache2.conf.tmp") + +- macro: plesk_running_mktemp + condition: (proc.name=mktemp and proc.aname[3] in (plesk_binaries)) + +- macro: networkmanager_writing_resolv_conf + condition: (proc.aname[2]=nm-dispatcher and fd.name=/etc/resolv.conf) + +- macro: add_shell_writing_shells_tmp + condition: (proc.name=add-shell and fd.name=/etc/shells.tmp) + +- macro: duply_writing_exclude_files + condition: (proc.name=touch and proc.pcmdline startswith "bash /usr/bin/duply" and fd.name startswith "/etc/duply") + +- macro: xmlcatalog_writing_files + condition: (proc.name=update-xmlcatal and fd.directory=/etc/xml) + +- macro: datadog_writing_conf + condition: ((proc.cmdline startswith "python /opt/datadog-agent" or + proc.cmdline startswith "entrypoint.sh /entrypoint.sh datadog start" or + proc.cmdline startswith "agent.py /opt/datadog-agent") + and fd.name startswith "/etc/dd-agent") + +- macro: rancher_writing_conf + condition: ((proc.name in (healthcheck, lb-controller, rancher-dns)) and + (container.image.repository contains "rancher/healthcheck" or + container.image.repository contains "rancher/lb-service-haproxy" or + container.image.repository contains "rancher/dns") and + (fd.name startswith "/etc/haproxy" or fd.name startswith "/etc/rancher-dns")) + +- macro: rancher_writing_root + condition: (proc.name=rancher-metadat and + (container.image.repository contains "rancher/metadata" or container.image.repository contains "rancher/lb-service-haproxy") and + fd.name startswith "/answers.json") + +- macro: checkpoint_writing_state + condition: (proc.name=checkpoint and + container.image.repository contains "coreos/pod-checkpointer" and + fd.name startswith "/etc/kubernetes") + +- macro: jboss_in_container_writing_passwd + condition: > + ((proc.cmdline="run-java.sh /opt/jboss/container/java/run/run-java.sh" + or proc.cmdline="run-java.sh /opt/run-java/run-java.sh") + and container + and fd.name=/etc/passwd) + +- macro: curl_writing_pki_db + condition: (proc.name=curl and fd.directory=/etc/pki/nssdb) + +- macro: haproxy_writing_conf + condition: ((proc.name in (update-haproxy-,haproxy_reload.) or proc.pname in (update-haproxy-,haproxy_reload,haproxy_reload.)) + and (fd.name=/etc/openvpn/client.map or fd.name startswith /etc/haproxy)) + +- macro: java_writing_conf + condition: (proc.exe endswith java and fd.name=/etc/.java/.systemPrefs/.system.lock) + +- macro: rabbitmq_writing_conf + condition: (proc.name=rabbitmq-server and fd.directory=/etc/rabbitmq) + +- macro: rook_writing_conf + condition: (proc.name=toolbox.sh and container.image.repository=rook/toolbox + and fd.directory=/etc/ceph) + +- macro: httpd_writing_conf_logs + condition: (proc.name=httpd and fd.name startswith /etc/httpd/) + +- macro: mysql_writing_conf + condition: > + ((proc.name in (start-mysql.sh, run-mysqld) or proc.pname=start-mysql.sh) and + (fd.name startswith /etc/mysql or fd.directory=/etc/my.cnf.d)) + +- macro: redis_writing_conf + condition: > + (proc.name in (run-redis, redis-launcher.) and (fd.name=/etc/redis.conf or fd.name startswith /etc/redis)) + +- macro: openvpn_writing_conf + condition: (proc.name in (openvpn,openvpn-entrypo) and fd.name startswith /etc/openvpn) + +- macro: php_handlers_writing_conf + condition: (proc.name=php_handlers_co and fd.name=/etc/psa/php_versions.json) + +- macro: sed_writing_temp_file + condition: > + ((proc.aname[3]=cron_start.sh and fd.name startswith /etc/security/sed) or + (proc.name=sed and (fd.name startswith /etc/apt/sources.list.d/sed or + fd.name startswith /etc/apt/sed or + fd.name startswith /etc/apt/apt.conf.d/sed))) + +- macro: cron_start_writing_pam_env + condition: (proc.cmdline="bash /usr/sbin/start-cron" and fd.name=/etc/security/pam_env.conf) + +# In some cases dpkg-reconfigur runs commands that modify /etc. Not +# putting the full set of package management programs yet. +- macro: dpkg_scripting + condition: (proc.aname[2] in (dpkg-reconfigur, dpkg-preconfigu)) + +- macro: ufw_writing_conf + condition: (proc.name=ufw and fd.directory=/etc/ufw) + +- macro: calico_writing_conf + condition: > + (((proc.name = calico-node) or + (container.image.repository=gcr.io/projectcalico-org/node and proc.name in (start_runit, cp)) or + (container.image.repository=gcr.io/projectcalico-org/cni and proc.name=sed)) + and fd.name startswith /etc/calico) + +- macro: prometheus_conf_writing_conf + condition: (proc.name=prometheus-conf and fd.name startswith /etc/prometheus/config_out) + +- macro: openshift_writing_conf + condition: (proc.name=oc and fd.name startswith /etc/origin/node) + +- macro: keepalived_writing_conf + condition: (proc.name in (keepalived, kube-keepalived) and fd.name=/etc/keepalived/keepalived.conf) + +- macro: etcd_manager_updating_dns + condition: (container and proc.name=etcd-manager and fd.name=/etc/hosts) + +- macro: automount_using_mtab + condition: (proc.pname = automount and fd.name startswith /etc/mtab) + +- macro: mcafee_writing_cma_d + condition: (proc.name=macompatsvc and fd.directory=/etc/cma.d) + +- macro: avinetworks_supervisor_writing_ssh + condition: > + (proc.cmdline="se_supervisor.p /opt/avi/scripts/se_supervisor.py -d" and + (fd.name startswith /etc/ssh/known_host_ or + fd.name startswith /etc/ssh/ssh_monitor_config_ or + fd.name startswith /etc/ssh/ssh_config_)) + +- macro: etckeeper_activities + condition: (never_true) + +- macro: etckeeper + condition: > + (proc.aname = etckeeper + or (proc.aname in (50vcs-commit, 30store-metadata, 50uncommitted-c)) + and (fd.name startswith /etc/.git/ + or fd.name = /etc/.etckeeper) + and etckeeper_activities) + +- macro: multipath_writing_conf + condition: (proc.name = multipath and fd.name startswith /etc/multipath/) + +# Add conditions to this macro (probably in a separate file, +# overwriting this macro) to allow for specific combinations of +# programs writing below specific directories below +# /etc. fluentd_writing_conf_files is a good example to follow, as it +# specifies both the program doing the writing as well as the specific +# files it is allowed to modify. +# +# In this file, it just takes one of the programs in the base macro +# and repeats it. +- macro: user_known_write_etc_conditions + condition: (proc.name=confd) + +# This is a placeholder for user to extend the whitelist for write below etc rule +- macro: user_known_write_below_etc_activities + condition: (never_true) + +- macro: calico_node + condition: (container.image.repository endswith calico/node and proc.name=calico-node) + +- macro: write_etc_common + condition: > + (open_write + and etc_dir and evt.dir=< + and proc_name_exists + and not proc.name in (passwd_binaries, shadowutils_binaries, sysdigcloud_binaries, + package_mgmt_binaries, ssl_mgmt_binaries, dhcp_binaries, + dev_creation_binaries, shell_mgmt_binaries, + mail_config_binaries, + sshkit_script_binaries, + ldconfig.real, ldconfig, confd, gpg, insserv, + apparmor_parser, update-mime, tzdata.config, tzdata.postinst, + systemd, systemd-machine, systemd-sysuser, + debconf-show, rollerd, bind9.postinst, sv, + gen_resolvconf., update-ca-certi, certbot, runsv, + qualys-cloud-ag, locales.postins, nomachine_binaries, + adclient, certutil, crlutil, pam-auth-update, parallels_insta, + openshift-launc, update-rc.d, puppet, falcoctl) + and not (container and proc.cmdline in ("cp /run/secrets/kubernetes.io/serviceaccount/ca.crt /etc/pki/ca-trust/source/anchors/openshift-ca.crt")) + and not proc.pname in (sysdigcloud_binaries, mail_config_binaries, hddtemp.postins, sshkit_script_binaries, locales.postins, deb_binaries, dhcp_binaries) + and not fd.name pmatch (safe_etc_dirs) + and not fd.name in (/etc/container_environment.sh, /etc/container_environment.json, /etc/motd, /etc/motd.svc) + and not sed_temporary_file + and not exe_running_docker_save + and not ansible_running_python + and not python_running_denyhosts + and not fluentd_writing_conf_files + and not user_known_write_etc_conditions + and not run_by_centrify + and not run_by_adclient + and not qualys_writing_conf_files + and not git_writing_nssdb + and not plesk_writing_keys + and not plesk_install_writing_apache_conf + and not plesk_running_mktemp + and not networkmanager_writing_resolv_conf + and not run_by_chef + and not add_shell_writing_shells_tmp + and not duply_writing_exclude_files + and not xmlcatalog_writing_files + and not parent_supervise_running_multilog + and not supervise_writing_status + and not pki_realm_writing_realms + and not htpasswd_writing_passwd + and not lvprogs_writing_conf + and not ovsdb_writing_openvswitch + and not datadog_writing_conf + and not curl_writing_pki_db + and not haproxy_writing_conf + and not java_writing_conf + and not dpkg_scripting + and not parent_ucf_writing_conf + and not rabbitmq_writing_conf + and not rook_writing_conf + and not php_handlers_writing_conf + and not sed_writing_temp_file + and not cron_start_writing_pam_env + and not httpd_writing_conf_logs + and not mysql_writing_conf + and not openvpn_writing_conf + and not consul_template_writing_conf + and not countly_writing_nginx_conf + and not ms_oms_writing_conf + and not ms_scx_writing_conf + and not azure_scripts_writing_conf + and not azure_networkwatcher_writing_conf + and not couchdb_writing_conf + and not update_texmf_writing_conf + and not slapadd_writing_conf + and not symantec_writing_conf + and not liveupdate_writing_conf + and not sosreport_writing_files + and not selinux_writing_conf + and not veritas_writing_config + and not nginx_writing_conf + and not nginx_writing_certs + and not chef_client_writing_conf + and not centrify_writing_krb + and not sssd_writing_krb + and not cockpit_writing_conf + and not ipsec_writing_conf + and not httpd_writing_ssl_conf + and not userhelper_writing_etc_security + and not pkgmgmt_progs_writing_pki + and not update_ca_trust_writing_pki + and not brandbot_writing_os_release + and not redis_writing_conf + and not openldap_writing_conf + and not ucpagent_writing_conf + and not iscsi_writing_conf + and not istio_writing_conf + and not ufw_writing_conf + and not calico_writing_conf + and not calico_writing_envvars + and not prometheus_conf_writing_conf + and not openshift_writing_conf + and not keepalived_writing_conf + and not rancher_writing_conf + and not checkpoint_writing_state + and not jboss_in_container_writing_passwd + and not etcd_manager_updating_dns + and not user_known_write_below_etc_activities + and not automount_using_mtab + and not mcafee_writing_cma_d + and not avinetworks_supervisor_writing_ssh + and not etckeeper + and not multipath_writing_conf + and not calico_node) + +- rule: Write below etc + desc: > + Trying to write to any file below /etc can serve as an auditing rule to track general system changes. + Such rules can be noisy and challenging to interpret, particularly if your system frequently undergoes updates. However, careful + profiling of your environment can transform this rule into an effective rule for detecting unusual behavior associated with system + changes, including compliance-related cases. + condition: write_etc_common + output: File below /etc opened for writing (file=%fd.name pcmdline=%proc.pcmdline gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: ERROR + tags: [maturity_sandbox, host, container, filesystem, mitre_persistence, T1098] + +- list: known_root_files + items: [/root/.monit.state, /root/.auth_tokens, /root/.bash_history, /root/.ash_history, /root/.aws/credentials, + /root/.viminfo.tmp, /root/.lesshst, /root/.bzr.log, /root/.gitconfig.lock, /root/.babel.json, /root/.localstack, + /root/.node_repl_history, /root/.mongorc.js, /root/.dbshell, /root/.augeas/history, /root/.rnd, /root/.wget-hsts, /health, /exec.fifo] + +- list: known_root_directories + items: [/root/.oracle_jre_usage, /root/.ssh, /root/.subversion, /root/.nami] + +- macro: known_root_conditions + condition: (fd.name startswith /root/orcexec. + or fd.name startswith /root/.m2 + or fd.name startswith /root/.npm + or fd.name startswith /root/.pki + or fd.name startswith /root/.ivy2 + or fd.name startswith /root/.config/Cypress + or fd.name startswith /root/.config/pulse + or fd.name startswith /root/.config/configstore + or fd.name startswith /root/jenkins/workspace + or fd.name startswith /root/.jenkins + or fd.name startswith /root/.cache + or fd.name startswith /root/.sbt + or fd.name startswith /root/.java + or fd.name startswith /root/.glide + or fd.name startswith /root/.sonar + or fd.name startswith /root/.v8flag + or fd.name startswith /root/infaagent + or fd.name startswith /root/.local/lib/python + or fd.name startswith /root/.pm2 + or fd.name startswith /root/.gnupg + or fd.name startswith /root/.pgpass + or fd.name startswith /root/.theano + or fd.name startswith /root/.gradle + or fd.name startswith /root/.android + or fd.name startswith /root/.ansible + or fd.name startswith /root/.crashlytics + or fd.name startswith /root/.dbus + or fd.name startswith /root/.composer + or fd.name startswith /root/.gconf + or fd.name startswith /root/.nv + or fd.name startswith /root/.local/share/jupyter + or fd.name startswith /root/oradiag_root + or fd.name startswith /root/workspace + or fd.name startswith /root/jvm + or fd.name startswith /root/.node-gyp) + +# Add conditions to this macro (probably in a separate file, +# overwriting this macro) to allow for specific combinations of +# programs writing below specific directories below +# / or /root. +# +# In this file, it just takes one of the condition in the base macro +# and repeats it. +- macro: user_known_write_root_conditions + condition: (fd.name=/root/.bash_history) + +# This is a placeholder for user to extend the whitelist for write below root rule +- macro: user_known_write_below_root_activities + condition: (never_true) + +- macro: runc_writing_exec_fifo + condition: (proc.cmdline="runc:[1:CHILD] init" and fd.name=/exec.fifo) + +- macro: runc_writing_var_lib_docker + condition: (proc.cmdline="runc:[1:CHILD] init" and evt.arg.filename startswith /var/lib/docker) + +- macro: mysqlsh_writing_state + condition: (proc.name=mysqlsh and fd.directory=/root/.mysqlsh) + +- rule: Write below root + desc: > + Trying to write to any file directly below / or /root can serve as an auditing rule to track general system changes. + Such rules can be noisy and challenging to interpret, particularly if your system frequently undergoes updates. However, careful + profiling of your environment can transform this rule into an effective rule for detecting unusual behavior associated with system + changes, including compliance-related cases. Lastly, this rule stands out as potentially the noisiest one among rules related + to "write below. + condition: > + open_write and evt.dir=< + and root_dir + and proc_name_exists + and not fd.name in (known_root_files) + and not fd.directory pmatch (known_root_directories) + and not exe_running_docker_save + and not gugent_writing_guestagent_log + and not dse_writing_tmp + and not zap_writing_state + and not airflow_writing_state + and not rpm_writing_root_rpmdb + and not maven_writing_groovy + and not chef_writing_conf + and not kubectl_writing_state + and not cassandra_writing_state + and not galley_writing_state + and not calico_writing_state + and not rancher_writing_root + and not runc_writing_exec_fifo + and not mysqlsh_writing_state + and not known_root_conditions + and not user_known_write_root_conditions + and not user_known_write_below_root_activities + output: File below / or /root opened for writing (file=%fd.name evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: ERROR + tags: [maturity_sandbox, host, container, filesystem, mitre_persistence, TA0003] + +- macro: amazon_linux_running_python_yum + condition: > + (proc.name = python and + proc.pcmdline = "python -m amazon_linux_extras system_motd" and + proc.cmdline startswith "python -c import yum;") + +- macro: user_known_write_rpm_database_activities + condition: (never_true) + +# Only let rpm-related programs write to the rpm database +- rule: Write below rpm database + desc: > + Trying to write to the rpm database by any non-rpm related program can serve as an auditing rule to track general system changes. + Such rules can be noisy and challenging to interpret, particularly if your system frequently undergoes updates. However, careful + profiling of your environment can transform this rule into an effective rule for detecting unusual behavior associated with system + changes, including compliance-related cases. + condition: > + open_write + and fd.name startswith /var/lib/rpm + and not rpm_procs + and not ansible_running_python + and not python_running_chef + and not exe_running_docker_save + and not amazon_linux_running_python_yum + and not user_known_write_rpm_database_activities + output: rpm database opened for writing by a non-rpm program (file=%fd.name pcmdline=%proc.pcmdline evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: ERROR + tags: [maturity_sandbox, host, container, filesystem, software_mgmt, mitre_persistence, T1072] + +- macro: user_known_modify_bin_dir_activities + condition: (never_true) + +- rule: Modify binary dirs + desc: > + Trying to modify any file below a set of binary directories can serve as an auditing rule to track general system changes. + Such rules can be noisy and challenging to interpret, particularly if your system frequently undergoes updates. However, careful + profiling of your environment can transform this rule into an effective rule for detecting unusual behavior associated with system + changes, including compliance-related cases. + condition: > + modify + and bin_dir_rename + and not package_mgmt_procs + and not exe_running_docker_save + and not user_known_modify_bin_dir_activities + output: File below known binary directory renamed/removed (file=%fd.name pcmdline=%proc.pcmdline evt_args=%evt.args evt_type=%evt.type evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: ERROR + tags: [maturity_sandbox, host, container, filesystem, mitre_defense_evasion, T1222.002] + +- macro: user_known_mkdir_bin_dir_activities + condition: (never_true) + +- rule: Mkdir binary dirs + desc: > + Trying to create a directory below a set of binary directories can serve as an auditing rule to track general system changes. + Such rules can be noisy and challenging to interpret, particularly if your system frequently undergoes updates. However, careful + profiling of your environment can transform this rule into an effective rule for detecting unusual behavior associated with system + changes, including compliance-related cases. + condition: > + mkdir + and bin_dir_mkdir + and not package_mgmt_procs + and not user_known_mkdir_bin_dir_activities + and not exe_running_docker_save + output: Directory below known binary directory created (directory=%evt.arg.path evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: ERROR + tags: [maturity_sandbox, host, container, filesystem, mitre_persistence, T1222.002] + +# https://docs.aws.amazon.com/eks/latest/userguide/add-ons-images.html +# official AWS EKS registry list. AWS has different ECR repo per region +- macro: allowed_aws_ecr_registry_root_for_eks + condition: > + (container.image.repository startswith "602401143452.dkr.ecr" or + container.image.repository startswith "877085696533.dkr.ecr" or + container.image.repository startswith "800184023465.dkr.ecr" or + container.image.repository startswith "918309763551.dkr.ecr" or + container.image.repository startswith "961992271922.dkr.ecr" or + container.image.repository startswith "590381155156.dkr.ecr" or + container.image.repository startswith "558608220178.dkr.ecr" or + container.image.repository startswith "151742754352.dkr.ecr" or + container.image.repository startswith "013241004608.dkr.ecr") + +- macro: aws_eks_image_sensitive_mount + condition: > + (allowed_aws_ecr_registry_root_for_eks and container.image.repository endswith ".amazonaws.com/amazon-k8s-cni") + +# These images are allowed both to run with --privileged and to mount +# sensitive paths from the host filesystem. +# +# NOTE: This list is only provided for backwards compatibility with +# older local falco rules files that may have been appending to +# trusted_images. To make customizations, it's better to add images to +# either privileged_images or falco_sensitive_mount_images. +- list: trusted_images + items: [] + +# Add conditions to this macro (probably in a separate file, +# overwriting this macro) to specify additional containers that are +# trusted and therefore allowed to run privileged *and* with sensitive +# mounts. +# +# Like trusted_images, this is deprecated in favor of +# user_privileged_containers and user_sensitive_mount_containers and +# is only provided for backwards compatibility. +# +# In this file, it just takes one of the images in trusted_containers +# and repeats it. +- macro: user_trusted_containers + condition: (never_true) + +# Falco containers +- list: falco_containers + items: + - falcosecurity/falco + - docker.io/falcosecurity/falco + - public.ecr.aws/falcosecurity/falco + +# Falco no driver containers + - falcosecurity/falco-no-driver + - docker.io/falcosecurity/falco-no-driver + - public.ecr.aws/falcosecurity/falco-no-driver + +# These container images are allowed to mount sensitive paths from the +# host filesystem. +- list: falco_sensitive_mount_images + items: [ + falco_containers, + docker.io/sysdig/sysdig, sysdig/sysdig, + gcr.io/google_containers/hyperkube, + gcr.io/google_containers/kube-proxy, docker.io/calico/node, + docker.io/rook/toolbox, docker.io/cloudnativelabs/kube-router, docker.io/consul, + docker.io/datadog/docker-dd-agent, docker.io/datadog/agent, docker.io/docker/ucp-agent, docker.io/gliderlabs/logspout, + docker.io/netdata/netdata, docker.io/google/cadvisor, docker.io/prom/node-exporter, + amazon/amazon-ecs-agent, prom/node-exporter, amazon/cloudwatch-agent + ] + +- macro: falco_sensitive_mount_containers + condition: (user_trusted_containers or + aws_eks_image_sensitive_mount or + container.image.repository in (trusted_images) or + container.image.repository in (falco_sensitive_mount_images) or + container.image.repository startswith quay.io/sysdig/ or + container.image.repository=k8scloudprovider/cinder-csi-plugin) + +# Add conditions to this macro (probably in a separate file, +# overwriting this macro) to specify additional containers that are +# allowed to perform sensitive mounts. +# +# In this file, it just takes one of the images in falco_sensitive_mount_images +# and repeats it. +- macro: user_sensitive_mount_containers + condition: (never_true) + +# For now, only considering a full mount of /etc as +# sensitive. Ideally, this would also consider all subdirectories +# below /etc as well, but the globbing mechanism +# doesn't allow exclusions of a full pattern, only single characters. +- macro: sensitive_mount + condition: (not container.mount.dest[/proc*] in ("","N/A") or + not container.mount.dest[/var/run/docker.sock] in ("","N/A") or + not container.mount.dest[/var/run/crio/crio.sock] in ("","N/A") or + not container.mount.dest[/run/containerd/containerd.sock] in ("","N/A") or + not container.mount.dest[/var/lib/kubelet] in ("","N/A") or + not container.mount.dest[/var/lib/kubelet/pki] in ("","N/A") or + not container.mount.dest[/] in ("","N/A") or + not container.mount.dest[/home/admin] in ("","N/A") or + not container.mount.dest[/etc] in ("","N/A") or + not container.mount.dest[/etc/kubernetes] in ("","N/A") or + not container.mount.dest[/etc/kubernetes/manifests] in ("","N/A") or + not container.mount.dest[/root*] in ("","N/A")) + +- rule: Launch Sensitive Mount Container + desc: > + Detect the initial process launched within a container that has a mount from a sensitive host directory (e.g. /proc). + Exceptions are made for known trusted images. This rule holds value for generic auditing; however, its noisiness + varies based on your environment. + condition: > + container_started + and container + and sensitive_mount + and not falco_sensitive_mount_containers + and not user_sensitive_mount_containers + output: Container with sensitive mount started (mounts=%container.mounts evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: INFO + tags: [maturity_sandbox, container, cis, mitre_execution, T1610] + +# In a local/user rules file, you could override this macro to +# explicitly enumerate the container images that you want to run in +# your environment. In this main falco rules file, there isn't any way +# to know all the containers that can run, so any container is +# allowed, by using a filter that is guaranteed to evaluate to true. +# In the overridden macro, the condition would look something like +# (container.image.repository = vendor/container-1 or +# container.image.repository = vendor/container-2 or ...) +- macro: allowed_containers + condition: (container.id exists) + +- rule: Launch Disallowed Container + desc: > + Detect the initial process launched within a container that is not in a list of allowed containers. + This rule holds value for generic auditing; however, this rule requires a good understanding of your + setup and consistent effort to keep the list of allowed containers current. In some situations, + this can be challenging to manage. + condition: > + container_started + and container + and not allowed_containers + output: Container started and not in allowed list (evt_type=%evt.type user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: WARNING + tags: [maturity_sandbox, container, mitre_lateral_movement, T1610] + +# In some environments, any attempt by a interpreted program (perl, +# python, ruby, etc) to listen for incoming connections or perform +# outgoing connections might be suspicious. These rules are not +# enabled by default. +- rule: Interpreted procs inbound network activity + desc: > + Any inbound network activity performed by any interpreted program (perl, python, ruby, etc.). While it offers broad coverage and behavioral + insights, operationalizing it effectively requires significant time and might result in a moderate level of noise. Suggesting customizing + this rule to be more specific. For example, you could set it up to alert only for important namespaces after studying their usual behavior. + condition: > + inbound + and interpreted_procs + enabled: false + output: Interpreted program received/listened for network traffic (connection=%fd.name lport=%fd.lport rport=%fd.rport fd_type=%fd.type fd_proto=fd.l4proto evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: NOTICE + tags: [maturity_sandbox, host, container, network, mitre_exfiltration, TA0011] + +- rule: Interpreted procs outbound network activity + desc: > + Any outbound network activity performed by any interpreted program (perl, python, ruby, etc.). While it offers broad coverage and behavioral + insights, operationalizing it effectively requires significant time and might result in a moderate level of noise. Suggesting customizing + this rule to be more specific. For example, you could set it up to alert only for important namespaces after studying their usual behavior. + condition: > + outbound + and interpreted_procs + enabled: false + output: Interpreted program performed outgoing network connection (connection=%fd.name lport=%fd.lport rport=%fd.rport fd_type=%fd.type fd_proto=fd.l4proto evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: NOTICE + tags: [maturity_sandbox, host, container, network, mitre_exfiltration, TA0011] + +# In a local/user rules file, list the container images that are +# allowed to contact NodePort services from within a container. This +# might cover cases where the K8s infrastructure itself is running +# within a container. +# +# By default, all containers are allowed to contact NodePort services. +- macro: nodeport_containers + condition: (never_true) + +- rule: Unexpected K8s NodePort Connection + desc: > + Detect attempts to utilize K8s NodePorts from a container. K8s NodePorts are accessible on the eth0 interface of + each node, and they facilitate external traffic into a Kubernetes cluster. Attackers could misuse them for + unauthorized access. The rule uses default port ranges, but check for custom ranges and make necessary adjustments. + Also, consider tuning this rule as needed. + condition: > + inbound_outbound + and container + and fd.sport >= 30000 + and fd.sport <= 32767 + and not nodeport_containers + enabled: false + output: Unexpected K8s NodePort Connection (connection=%fd.name lport=%fd.lport rport=%fd.rport fd_type=%fd.type fd_proto=fd.l4proto evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: NOTICE + tags: [maturity_sandbox, network, k8s, container, mitre_persistence, T1205.001, NIST_800-53_AC-6] + +- list: exclude_hidden_directories + items: [/root/.cassandra] + +# The rule is disabled by default. +- macro: user_known_create_hidden_file_activities + condition: (never_true) + +- rule: Create Hidden Files or Directories + desc: > + Detecting hidden files or directories creation can serve as an auditing rule to track general system changes. + Such rules can be noisy and challenging to interpret, particularly if your system frequently undergoes updates. However, careful + profiling of your environment can transform this rule into an effective rule for detecting unusual behavior associated with system + changes, including compliance-related cases. + condition: > + ((modify and evt.arg.newpath contains "/.") or + (mkdir and evt.arg.path contains "/.") or + (open_write and evt.arg.flags contains "O_CREAT" and fd.name contains "/." and not fd.name pmatch (exclude_hidden_directories))) + and not user_known_create_hidden_file_activities + and not exe_running_docker_save + enabled: false + output: Hidden file or directory created (file=%fd.name newpath=%evt.arg.newpath evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: + NOTICE + tags: [maturity_sandbox, host, container, filesystem, mitre_defense_evasion, T1564.001] + +- list: miner_ports + items: [ + 25, 3333, 3334, 3335, 3336, 3357, 4444, + 5555, 5556, 5588, 5730, 6099, 6666, 7777, + 7778, 8000, 8001, 8008, 8080, 8118, 8333, + 8888, 8899, 9332, 9999, 14433, 14444, + 45560, 45700 + ] + +- list: miner_domains + items: [ + "asia1.ethpool.org","ca.minexmr.com", + "cn.stratum.slushpool.com","de.minexmr.com", + "eth-ar.dwarfpool.com","eth-asia.dwarfpool.com", + "eth-asia1.nanopool.org","eth-au.dwarfpool.com", + "eth-au1.nanopool.org","eth-br.dwarfpool.com", + "eth-cn.dwarfpool.com","eth-cn2.dwarfpool.com", + "eth-eu.dwarfpool.com","eth-eu1.nanopool.org", + "eth-eu2.nanopool.org","eth-hk.dwarfpool.com", + "eth-jp1.nanopool.org","eth-ru.dwarfpool.com", + "eth-ru2.dwarfpool.com","eth-sg.dwarfpool.com", + "eth-us-east1.nanopool.org","eth-us-west1.nanopool.org", + "eth-us.dwarfpool.com","eth-us2.dwarfpool.com", + "eu.stratum.slushpool.com","eu1.ethermine.org", + "eu1.ethpool.org","fr.minexmr.com", + "mine.moneropool.com","mine.xmrpool.net", + "pool.minexmr.com","pool.monero.hashvault.pro", + "pool.supportxmr.com","sg.minexmr.com", + "sg.stratum.slushpool.com","stratum-eth.antpool.com", + "stratum-ltc.antpool.com","stratum-zec.antpool.com", + "stratum.antpool.com","us-east.stratum.slushpool.com", + "us1.ethermine.org","us1.ethpool.org", + "us2.ethermine.org","us2.ethpool.org", + "xmr-asia1.nanopool.org","xmr-au1.nanopool.org", + "xmr-eu1.nanopool.org","xmr-eu2.nanopool.org", + "xmr-jp1.nanopool.org","xmr-us-east1.nanopool.org", + "xmr-us-west1.nanopool.org","xmr.crypto-pool.fr", + "xmr.pool.minergate.com", "rx.unmineable.com", + "ss.antpool.com","dash.antpool.com", + "eth.antpool.com","zec.antpool.com", + "xmc.antpool.com","btm.antpool.com", + "stratum-dash.antpool.com","stratum-xmc.antpool.com", + "stratum-btm.antpool.com" + ] + +- list: https_miner_domains + items: [ + "ca.minexmr.com", + "cn.stratum.slushpool.com", + "de.minexmr.com", + "fr.minexmr.com", + "mine.moneropool.com", + "mine.xmrpool.net", + "pool.minexmr.com", + "sg.minexmr.com", + "stratum-eth.antpool.com", + "stratum-ltc.antpool.com", + "stratum-zec.antpool.com", + "stratum.antpool.com", + "xmr.crypto-pool.fr", + "ss.antpool.com", + "stratum-dash.antpool.com", + "stratum-xmc.antpool.com", + "stratum-btm.antpool.com", + "btm.antpool.com" + ] + +- list: http_miner_domains + items: [ + "ca.minexmr.com", + "de.minexmr.com", + "fr.minexmr.com", + "mine.moneropool.com", + "mine.xmrpool.net", + "pool.minexmr.com", + "sg.minexmr.com", + "xmr.crypto-pool.fr" + ] + +# Add rule based on crypto mining IOCs +- macro: minerpool_https + condition: (fd.sport="443" and fd.sip.name in (https_miner_domains)) + +- macro: minerpool_http + condition: (fd.sport="80" and fd.sip.name in (http_miner_domains)) + +- macro: minerpool_other + condition: (fd.sport in (miner_ports) and fd.sip.name in (miner_domains)) + +- macro: net_miner_pool + condition: (evt.type in (sendto, sendmsg, connect) and evt.dir=< and (fd.net != "127.0.0.0/8" and not fd.snet in (rfc_1918_addresses)) and ((minerpool_http) or (minerpool_https) or (minerpool_other))) + +- macro: trusted_images_query_miner_domain_dns + condition: (container.image.repository in (falco_containers)) + +# The rule is disabled by default. +# Note: falco will send DNS request to resolve miner pool domain which may trigger alerts in your environment. +- rule: Detect outbound connections to common miner pool ports + desc: > + Miners usually connect to miner pools using standard ports, and this rule flags such activity. Important: Falco currently sends DNS + requests to resolve miner pool domains, which could trigger other alerts. Prior to enabling this rule, it's advised to ensure whether + this is acceptable for your environment. This rule is specifically disabled for that reason. + condition: > + net_miner_pool + and not trusted_images_query_miner_domain_dns + enabled: false + output: Outbound connection to IP/Port flagged by https://cryptoioc.ch (ip=%fd.rip connection=%fd.name lport=%fd.lport rport=%fd.rport fd_type=%fd.type fd_proto=fd.l4proto evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: CRITICAL + tags: [maturity_sandbox, host, container, network, mitre_impact, T1496] + +- rule: Detect crypto miners using the Stratum protocol + desc: > + Miners commonly specify the mining pool to connect to using a URI that starts with "stratum+tcp". However, this rule is highly specific to + this technique, and matching command-line arguments can generally be bypassed quite easily. + condition: > + spawned_process + and (proc.cmdline contains "stratum+tcp" or + proc.cmdline contains "stratum2+tcp" or + proc.cmdline contains "stratum+ssl" or + proc.cmdline contains "stratum2+ssl") + output: Possible miner running (evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) + priority: CRITICAL + tags: [maturity_sandbox, host, container, process, mitre_impact, T1496] + +- list: k8s_client_binaries + items: [docker, kubectl, crictl] + +- list: user_known_k8s_ns_kube_system_images + items: [ + registry.k8s.io/fluentd-gcp-scaler, + registry.k8s.io/node-problem-detector/node-problem-detector + ] + +- list: user_known_k8s_images + items: [ + mcr.microsoft.com/aks/hcp/hcp-tunnel-front + ] + +# Whitelist for known docker client binaries run inside container +# - registry.k8s.io/fluentd-gcp-scaler in GCP/GKE +- macro: user_known_k8s_client_container + condition: > + (k8s.ns.name="kube-system" and container.image.repository in (user_known_k8s_ns_kube_system_images)) or container.image.repository in (user_known_k8s_images) + +- macro: user_known_k8s_client_container_parens + condition: (user_known_k8s_client_container) + +- rule: Kubernetes Client Tool Launched in Container + desc: > + Detect the execution of a Kubernetes client tool (like docker, kubectl, crictl) within a container, which is typically not expected behavior. + Although this rule targets container workloads, monitoring the use of tools like crictl on the host over interactive access could also be + valuable for broader auditing objectives. + condition: > + spawned_process + and container + and not user_known_k8s_client_container_parens + and proc.name in (k8s_client_binaries) + output: Kubernetes Client Tool Launched in Container (evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) + priority: WARNING + tags: [maturity_sandbox, container, mitre_execution, T1610] + +# The two Container Drift rules below will fire when a new executable is created in a container. +# There are two ways to create executables - file is created with execution permissions or permissions change of existing file. +# We will use a new filter, is_open_exec, to find all files creations with execution permission, and will trace all chmods in a container. +# The use case we are targeting here is an attempt to execute code that was not shipped as part of a container (drift) - +# an activity that might be malicious or non-compliant. +# Two things to pay attention to: +# 1) In most cases, 'docker cp' will not be identified, but the assumption is that if an attacker gained access to the container runtime daemon, they are already privileged +# 2) Drift rules will be noisy in environments in which containers are built (e.g. docker build) +# These two rules are not enabled by default. +- macro: user_known_container_drift_activities + condition: (never_true) + +- rule: Container Drift Detected (chmod) + desc: > + Detect new executables created within a container as a result of chmod. While this detection can generate significant noise, chmod + usage is frequently linked to dropping and executing malicious implants. The newer rule "Drop and execute new binary in container" + provides more precise detection of this TTP using unambiguous kernel signals. It is recommended to use the new rule. However, this + rule might be more relevant for auditing if applicable in your environment, such as when chmod is used on files within the /tmp folder. + condition: > + chmod + and container + and evt.rawres>=0 + and ((evt.arg.mode contains "S_IXUSR") or + (evt.arg.mode contains "S_IXGRP") or + (evt.arg.mode contains "S_IXOTH")) + and not runc_writing_exec_fifo + and not runc_writing_var_lib_docker + and not user_known_container_drift_activities + enabled: false + output: Drift detected (chmod), new executable created in a container (filename=%evt.arg.filename name=%evt.arg.name mode=%evt.arg.mode evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: ERROR + tags: [maturity_sandbox, container, process, filesystem, mitre_execution, T1059] + +# **************************************************************************** +# * "Container Drift Detected (open+create)" requires FALCO_ENGINE_VERSION 6 * +# **************************************************************************** +- rule: Container Drift Detected (open+create) + desc: > + Detect new executables created within a container as a result of open+create. The newer rule "Drop and execute new binary in container" + provides more precise detection of this TTP using unambiguous kernel signals. It is recommended to use the new rule. + condition: > + evt.type in (open,openat,openat2,creat) + and evt.rawres>=0 + and evt.is_open_exec=true + and container + and not runc_writing_exec_fifo + and not runc_writing_var_lib_docker + and not user_known_container_drift_activities + enabled: false + output: Drift detected (open+create), new executable created in a container (filename=%evt.arg.filename name=%evt.arg.name mode=%evt.arg.mode evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: ERROR + tags: [maturity_sandbox, container, process, filesystem, mitre_execution, T1059] + +- list: run_as_root_image_list + items: [] + +- macro: user_known_run_as_root_container + condition: (container.image.repository in (run_as_root_image_list)) + +# The rule is disabled by default and should be enabled when non-root container policy has been applied. +# Note the rule will not work as expected when usernamespace is applied, e.g. userns-remap is enabled. +- rule: Container Run as Root User + desc: > + Container detected running as the root user. This should be taken into account especially when policies disallow containers from running with + root user privileges. Note that a root user in containers doesn't inherently possess extensive power, as modern container environments define + privileges through Linux capabilities. To learn more, check out the rule "Launch Privileged Container". + condition: > + spawned_process + and container + and proc.vpid=1 + and user.uid=0 + and not user_known_run_as_root_container + enabled: false + output: Container launched with root user privilege (evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) + priority: INFO + tags: [maturity_sandbox, container, process, users, mitre_execution, T1610] + +# This rule helps detect CVE-2021-3156: +# A privilege escalation to root through heap-based buffer overflow +- rule: Sudo Potential Privilege Escalation + desc: > + Affecting sudo (<= 1.9.5p2), there's a privilege escalation vulnerability. By executing sudo using the sudoedit -s or sudoedit -i command with a + command-line argument that ends with a single backslash character, an unprivileged user can potentially escalate privileges to root. This rule is + highly specific and might be bypassed due to potential issues with string matching on command line arguments. + condition: > + spawned_process + and user.uid != 0 + and (proc.name=sudoedit or proc.name = sudo) + and (proc.args contains -s or proc.args contains -i or proc.args contains --login) + and (proc.args contains "\ " or proc.args endswith \) + output: Detect Sudo Privilege Escalation Exploit (CVE-2021-3156) (evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) + priority: CRITICAL + tags: [maturity_sandbox, host, container, filesystem, users, mitre_privilege_escalation, T1548.003] + +- list: user_known_userfaultfd_processes + items: [] + +- rule: Unprivileged Delegation of Page Faults Handling to a Userspace Process + desc: > + Detect a successful unprivileged userfaultfd syscall, which could serve as an attack primitive for exploiting other vulnerabilities. + To fine-tune this rule, consider using the template list "user_known_userfaultfd_processes". + condition: > + evt.type = userfaultfd + and user.uid != 0 + and (evt.rawres >= 0 or evt.res != -1) + and not proc.name in (user_known_userfaultfd_processes) + output: An userfaultfd syscall was successfully executed by an unprivileged user (evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: CRITICAL + tags: [maturity_sandbox, host, container, process, mitre_defense_evasion, TA0005] + +# This rule helps detect CVE-2021-4034: +# A privilege escalation to root through memory corruption +- rule: Polkit Local Privilege Escalation Vulnerability (CVE-2021-4034) + desc: > + This rule detects attempts to exploit a privilege escalation vulnerability in Polkit's pkexec. Through the execution of specially + crafted code, a local user can exploit this weakness to attain root privileges on a compromised system. This rule is highly + specific in its scope. + condition: + spawned_process + and user.uid != 0 + and proc.name=pkexec + and proc.args = '' + output: Detect Polkit pkexec Local Privilege Escalation Exploit (CVE-2021-4034) (args=%proc.args evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) + priority: CRITICAL + tags: [maturity_sandbox, host, container, process, users, mitre_privilege_escalation, TA0004] + +# Rule for detecting potential Log4Shell (CVE-2021-44228) exploitation +# Note: Not compatible with Java 17+, which uses read() syscalls +- macro: java_network_read + condition: (evt.type=recvfrom and fd.type in (ipv4, ipv6) and proc.exe endswith java) + +- rule: Java Process Class File Download + desc: > + Detecting a Java process downloading a class file which could indicate a successful exploit of the log4shell Log4j vulnerability (CVE-2021-44228). + This rule is highly specific in its scope. + condition: > + java_network_read + and evt.buffer bcontains cafebabe + output: Java process class file download (server_ip=%fd.sip server_port=%fd.sport connection=%fd.name lport=%fd.lport rport=%fd.rport fd_type=%fd.type fd_proto=fd.l4proto evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: CRITICAL + enabled: false + tags: [maturity_sandbox, host, container, process, mitre_initial_access, T1190] + +- list: docker_binaries + items: [docker, dockerd, containerd-shim, "runc:[1:CHILD]", pause, exe, docker-compose, docker-entrypoi, docker-runc-cur, docker-current, dockerd-current] + +- macro: docker_procs + condition: proc.name in (docker_binaries) + +- rule: Modify Container Entrypoint + desc: > + This rule detect an attempt to write on container entrypoint symlink (/proc/self/exe). Possible CVE-2019-5736 Container Breakout exploitation attempt. + This rule has a more narrow scope. + condition: > + open_write + and container + and (fd.name=/proc/self/exe or fd.name startswith /proc/self/fd/) + and not docker_procs + and not proc.cmdline = "runc:[1:CHILD] init" + enabled: false + output: Detect Potential Container Breakout Exploit (CVE-2019-5736) (file=%fd.name evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: WARNING + tags: [maturity_sandbox, container, filesystem, mitre_initial_access, T1611] + +- list: known_decode_payload_containers + items: [] + +- macro: base64_decoding + condition: (proc.cmdline contains "base64" and (proc.cmdline contains "--decode" or proc.cmdline contains "-d")) + +- rule: Decoding Payload in Container + desc: > + Detect any use of {base64} decoding in a container. Legitimate applications may decode encoded payloads. The template list + known_decode_payload_containers can be used for simple tuning and customization, or you can adopt custom, more refined tuning. Less + sophisticated adversaries may {base64}-decode their payloads not only to obfuscate them, but also to ensure that the payload remains + intact when the application processes it. Note that injecting commands into an application's input often results in the application + processing passed strings like "sh -c". In these cases, you may be lucky and the encoded blob will also be logged. Otherwise, all you + will see is the {base64} decoding command, as the encoded blob was already interpreted by the shell. + condition: > + spawned_process + and container + and base64_decoding + and not container.image.repository in (known_decode_payload_containers) + output: Decoding Payload in Container (evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) + priority: INFO + tags: [maturity_sandbox, container, process, mitre_command_and_control, T1132] +- list: recon_binaries + items: [w, whoami, id, who, uname] + +- condition: (proc.name in (recon_binaries)) + macro: recon_binaries_procs + +- rule: Basic Interactive Reconnaissance + desc: > + This rule detects basic interactive reconnaissance commands that are typically run by unsophisticated attackers or used + in internal Red Team exercises. Interactive is defined as a terminal being present (proc.tty != 0). This could be any + form of reverse shell or usage of kubectl exec or ssh etc. In addition, filtering for the process being the process group + leader indicates that the command was "directly" typed into the terminal and not run as a result of a script. This rule + is a basic auditing or template rule. You can expand the list of reconnaissance commands, such as by adding "ls". Common + anti-patterns are SRE activity or debugging, but it is still worth capturing this generically. Typically, you would expect + other rules to fire as well in relation to this activity. + condition: > + spawned_process + and recon_binaries_procs + and proc.tty != 0 + and proc.is_vpgid_leader=true + output: Basic Interactive Reconnaissance (evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) + priority: NOTICE + tags: [maturity_sandbox, host, container, process, mitre_reconnaissance, TA0043] diff --git a/rules/falco_rules.yaml b/rules/falco_rules.yaml index df32ca37e..f25108cc9 100644 --- a/rules/falco_rules.yaml +++ b/rules/falco_rules.yaml @@ -1,5 +1,6 @@ +# SPDX-License-Identifier: Apache-2.0 # -# Copyright (C) 2022 The Falco Authors. +# Copyright (C) 2023 The Falco Authors. # # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,9 +16,16 @@ # limitations under the License. # +# Information about rules tags and fields can be found here: https://falco.org/docs/rules/#tags-for-current-falco-ruleset +# The initial item in the `tags` fields reflects the maturity level of the rules introduced upon the proposal https://github.com/falcosecurity/rules/blob/main/proposals/20230605-rules-adoption-management-maturity-framework.md +# `tags` fields also include information about the type of workload inspection (host and/or container), and Mitre Attack killchain phases and Mitre TTP code(s) +# Mitre Attack References: +# [1] https://attack.mitre.org/tactics/enterprise/ +# [2] https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json + # Starting with version 8, the Falco engine supports exceptions. # However the Falco rules file does not use them by default. -- required_engine_version: 17 +- required_engine_version: 0.31.0 # Currently disabled as read/write are ignored syscalls. The nearly # similar open_write/open_read check for files being opened for @@ -27,21 +35,12 @@ # - macro: read # condition: (syscall.type=read and evt.dir=> and fd.type in (file, directory)) -# Information about rules tags and fields can be found here: https://falco.org/docs/rules/#tags-for-current-falco-ruleset -# `tags` fields also include information about the type of workload inspection, Mitre Attack killchain phases and Mitre TTP code(s) -# Mitre Attack References: -# [1] https://attack.mitre.org/tactics/enterprise/ -# [2] https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json - - macro: open_write condition: (evt.type in (open,openat,openat2) and evt.is_open_write=true and fd.typechar='f' and fd.num>=0) - macro: open_read condition: (evt.type in (open,openat,openat2) and evt.is_open_read=true and fd.typechar='f' and fd.num>=0) -- macro: open_directory - condition: (evt.type in (open,openat,openat2) and evt.is_open_read=true and fd.typechar='d' and fd.num>=0) - # Failed file open attempts, useful to detect threat actors making mistakes # https://man7.org/linux/man-pages/man3/errno.3.html # evt.res=ENOENT - No such file or directory @@ -49,35 +48,35 @@ - macro: open_file_failed condition: (evt.type in (open,openat,openat2) and fd.typechar='f' and fd.num=-1 and evt.res startswith E) +# This macro `never_true` is used as placeholder for tuning negative logical sub-expressions, for example +# - macro: allowed_ssh_hosts +# condition: (never_true) +# can be used in a rules' expression with double negation `and not allowed_ssh_hosts` which effectively evaluates +# to true and does nothing, the perfect empty template for `logical` cases as opposed to list templates. +# When tuning the rule you can override the macro with something useful, e.g. +# - macro: allowed_ssh_hosts +# condition: (evt.hostname contains xyz) - macro: never_true condition: (evt.num=0) -- macro: always_true - condition: (evt.num>=0) +# This macro `always_true` is the flip side of the macro `never_true` and currently is commented out as +# it is not used. You can use it as placeholder for a positive logical sub-expression tuning template +# macro, e.g. `and custom_procs`, where +# - macro: custom_procs +# condition: (always_true) +# later you can customize, override the macros to something like +# - macro: custom_procs +# condition: (proc.name in (custom1, custom2, custom3)) +# - macro: always_true +# condition: (evt.num>=0) # In some cases, such as dropped system call events, information about # the process name may be missing. For some rules that really depend # on the identity of the process performing an action such as opening # a file, etc., we require that the process name be known. +# TODO: At the moment we keep the `N/A` variant for compatibility with old scap-files - macro: proc_name_exists - condition: (proc.name!="") - -- macro: rename - condition: (evt.type in (rename, renameat, renameat2)) - -- macro: mkdir - condition: (evt.type in (mkdir, mkdirat)) - -- macro: remove - condition: (evt.type in (rmdir, unlink, unlinkat)) - -- macro: modify - condition: (rename or remove) - -# %evt.arg.flags available for evt.dir=>, but only for umount2 -# %evt.arg.name is path and available for evt.dir=< -# - macro: umount -# condition: (evt.type in (umount, umount2)) + condition: (not proc.name in ("","N/A")) - macro: spawned_process condition: (evt.type in (execve, execveat) and evt.dir=<) @@ -88,78 +87,21 @@ - macro: create_hardlink condition: (evt.type in (link, linkat) and evt.dir=<) -- macro: chmod - condition: (evt.type in (chmod, fchmod, fchmodat) and evt.dir=<) - - macro: kernel_module_load condition: (evt.type in (init_module, finit_module) and evt.dir=<) -# File categories -- macro: bin_dir - condition: (fd.directory in (/bin, /sbin, /usr/bin, /usr/sbin)) - -- macro: bin_dir_mkdir - condition: > - (evt.arg.path startswith /bin/ or - evt.arg.path startswith /sbin/ or - evt.arg.path startswith /usr/bin/ or - evt.arg.path startswith /usr/sbin/) - -- macro: bin_dir_rename - condition: > - (evt.arg.path startswith /bin/ or - evt.arg.path startswith /sbin/ or - evt.arg.path startswith /usr/bin/ or - evt.arg.path startswith /usr/sbin/ or - evt.arg.name startswith /bin/ or - evt.arg.name startswith /sbin/ or - evt.arg.name startswith /usr/bin/ or - evt.arg.name startswith /usr/sbin/ or - evt.arg.oldpath startswith /bin/ or - evt.arg.oldpath startswith /sbin/ or - evt.arg.oldpath startswith /usr/bin/ or - evt.arg.oldpath startswith /usr/sbin/ or - evt.arg.newpath startswith /bin/ or - evt.arg.newpath startswith /sbin/ or - evt.arg.newpath startswith /usr/bin/ or - evt.arg.newpath startswith /usr/sbin/) +- macro: dup + condition: (evt.type in (dup, dup2, dup3)) +# File categories - macro: etc_dir condition: (fd.name startswith /etc/) -# This detects writes immediately below / or any write anywhere below /root -- macro: root_dir - condition: (fd.directory=/ or fd.name startswith /root/) - - list: shell_binaries items: [ash, bash, csh, ksh, sh, tcsh, zsh, dash] -- list: ssh_binaries - items: [ - sshd, sftp-server, ssh-agent, - ssh, scp, sftp, - ssh-keygen, ssh-keysign, ssh-keyscan, ssh-add - ] - -- list: shell_mgmt_binaries - items: [add-shell, remove-shell] - - macro: shell_procs - condition: proc.name in (shell_binaries) - -- list: coreutils_binaries - items: [ - truncate, sha1sum, numfmt, fmt, fold, uniq, cut, who, - groups, csplit, sort, expand, printf, printenv, unlink, tee, chcon, stat, - basename, split, nice, "yes", whoami, sha224sum, hostid, users, stdbuf, - base64, unexpand, cksum, od, paste, nproc, pathchk, sha256sum, wc, test, - comm, arch, du, factor, sha512sum, md5sum, tr, runcon, env, dirname, - tsort, join, shuf, install, logname, pinky, nohup, expr, pr, tty, timeout, - tail, "[", seq, sha384sum, nl, head, id, mkfifo, sum, dircolors, ptx, shred, - tac, link, chroot, vdir, chown, touch, ls, dd, uname, "true", pwd, date, - chgrp, chmod, mktemp, cat, mknod, sync, ln, "false", rm, mv, cp, echo, - readlink, sleep, stty, mkdir, df, dir, rmdir, touch - ] + condition: (proc.name in (shell_binaries)) # dpkg -L login | grep bin | xargs ls -ld | grep -v '^d' | awk '{print $9}' | xargs -L 1 basename | tr "\\n" "," - list: login_binaries @@ -186,15 +128,6 @@ newusers, pwck, pwconv, pwunconv, useradd, userdel, usermod, vigr, vipw, unix_chkpwd ] -- list: sysdigcloud_binaries - items: [setup-backend, dragent, sdchecks] - -- list: k8s_binaries - items: [hyperkube, skydns, kube2sky, exechealthz, weave-net, loopback, bridge, openshift-sdn, openshift] - -- list: lxd_binaries - items: [lxd, lxcfs] - - list: http_server_binaries items: [nginx, httpd, httpd-foregroun, lighttpd, apache, apache2] @@ -210,15 +143,8 @@ - list: gitlab_binaries items: [gitlab-shell, gitlab-mon, gitlab-runner-b, git] -- list: interpreted_binaries - items: [lua, node, perl, perl5, perl6, php, python, python2, python3, ruby, tcl] - -- macro: interpreted_procs - condition: > - (proc.name in (interpreted_binaries)) - - macro: server_procs - condition: proc.name in (http_server_binaries, db_server_binaries, docker_binaries, sshd) + condition: (proc.name in (http_server_binaries, db_server_binaries, docker_binaries, sshd)) # The explicit quotes are needed to avoid the - characters being # interpreted by the filter expression. @@ -227,62 +153,30 @@ repoquery, rpmkeys, rpmq, yum-cron, yum-config-mana, yum-debug-dump, abrt-action-sav, rpmdb_stat, microdnf, rhn_check, yumdb] -- list: openscap_rpm_binaries - items: [probe_rpminfo, probe_rpmverify, probe_rpmverifyfile, probe_rpmverifypackage] - -- macro: rpm_procs - condition: (proc.name in (rpm_binaries, openscap_rpm_binaries) or proc.name in (salt-call, salt-minion)) - - list: deb_binaries items: [dpkg, dpkg-preconfigu, dpkg-reconfigur, dpkg-divert, apt, apt-get, aptitude, frontend, preinst, add-apt-reposit, apt-auto-remova, apt-key, apt-listchanges, unattended-upgr, apt-add-reposit, apt-cache, apt.systemd.dai ] +- list: python_package_managers + items: [pip, pip3, conda] # The truncated dpkg-preconfigu is intentional, process names are # truncated at the falcosecurity-libs level. - list: package_mgmt_binaries - items: [rpm_binaries, deb_binaries, update-alternat, gem, npm, pip, pip3, sane-utils.post, alternatives, chef-client, apk, snapd] - -- macro: package_mgmt_procs - condition: proc.name in (package_mgmt_binaries) - -- macro: package_mgmt_ancestor_procs - condition: proc.pname in (package_mgmt_binaries) or - proc.aname[2] in (package_mgmt_binaries) or - proc.aname[3] in (package_mgmt_binaries) or - proc.aname[4] in (package_mgmt_binaries) - -- macro: coreos_write_ssh_dir - condition: (proc.name=update-ssh-keys and fd.name startswith /home/core/.ssh) + items: [rpm_binaries, deb_binaries, update-alternat, gem, npm, python_package_managers, sane-utils.post, alternatives, chef-client, apk, snapd] - macro: run_by_package_mgmt_binaries - condition: proc.aname in (package_mgmt_binaries, needrestart) - -- list: ssl_mgmt_binaries - items: [ca-certificates] - -- list: dhcp_binaries - items: [dhclient, dhclient-script, 11-dhclient] + condition: (proc.aname in (package_mgmt_binaries, needrestart)) # A canonical set of processes that run other programs with different # privileges or as a different user. - list: userexec_binaries items: [sudo, su, suexec, critical-stack, dzdo] -- list: known_setuid_binaries - items: [ - sshd, dbus-daemon-lau, ping, ping6, critical-stack-, pmmcli, - filemng, PassengerAgent, bwrap, osdetect, nginxmng, sw-engine-fpm, - start-stop-daem - ] - - list: user_mgmt_binaries items: [login_binaries, passwd_binaries, shadowutils_binaries] -- list: dev_creation_binaries - items: [blkid, rename_device, update_engine, sgdisk] - - list: hids_binaries items: [aide, aide.wrapper, update-aide.con, logcheck, syslog-summary, osqueryd, ossec-syscheckd] @@ -292,9 +186,6 @@ - list: nomachine_binaries items: [nxexec, nxnode.bin, nxserver.bin, nxclient.bin] -- macro: system_procs - condition: proc.name in (coreutils_binaries, user_mgmt_binaries) - - list: mail_binaries items: [ sendmail, sendmail-msp, postfix, procmail, exim4, @@ -317,227 +208,15 @@ - macro: sensitive_files condition: > - fd.name startswith /etc and - (fd.name in (sensitive_file_names) - or fd.directory in (/etc/sudoers.d, /etc/pam.d)) + ((fd.name startswith /etc and fd.name in (sensitive_file_names)) or + fd.directory in (/etc/sudoers.d, /etc/pam.d)) # Indicates that the process is new. Currently detected using time # since process was started, using a threshold of 5 seconds. - macro: proc_is_new - condition: proc.duration <= 5000000000 - -# Network -- macro: inbound - condition: > - (((evt.type in (accept,accept4,listen) and evt.dir=<) or - (evt.type in (recvfrom,recvmsg) and evt.dir=< and - fd.l4proto != tcp and fd.connected=false and fd.name_changed=true)) and - (fd.typechar = 4 or fd.typechar = 6) and - (fd.ip != "0.0.0.0" and fd.net != "127.0.0.0/8") and - (evt.rawres >= 0 or evt.res = EINPROGRESS)) - -# RFC1918 addresses were assigned for private network usage -- list: rfc_1918_addresses - items: ['"10.0.0.0/8"', '"172.16.0.0/12"', '"192.168.0.0/16"'] - -- macro: outbound - condition: > - (((evt.type = connect and evt.dir=<) or - (evt.type in (sendto,sendmsg) and evt.dir=< and - fd.l4proto != tcp and fd.connected=false and fd.name_changed=true)) and - (fd.typechar = 4 or fd.typechar = 6) and - (fd.ip != "0.0.0.0" and fd.net != "127.0.0.0/8" and not fd.snet in (rfc_1918_addresses)) and - (evt.rawres >= 0 or evt.res = EINPROGRESS)) - -# Very similar to inbound/outbound, but combines the tests together -# for efficiency. -- macro: inbound_outbound - condition: > - ((((evt.type in (accept,accept4,listen,connect) and evt.dir=<)) and - (fd.typechar = 4 or fd.typechar = 6)) and - (fd.ip != "0.0.0.0" and fd.net != "127.0.0.0/8") and - (evt.rawres >= 0 or evt.res = EINPROGRESS)) - -- macro: ssh_port - condition: fd.sport=22 - -# In a local/user rules file, you could override this macro to -# enumerate the servers for which ssh connections are allowed. For -# example, you might have a ssh gateway host for which ssh connections -# are allowed. -# -# In the main falco rules file, there isn't any way to know the -# specific hosts for which ssh access is allowed, so this macro just -# repeats ssh_port, which effectively allows ssh from all hosts. In -# the overridden macro, the condition would look something like -# "fd.sip="a.b.c.d" or fd.sip="e.f.g.h" or ..." -- macro: allowed_ssh_hosts - condition: ssh_port - -- rule: Disallowed SSH Connection - desc: Detect any new ssh connection to a host other than those in an allowed group of hosts - condition: (inbound_outbound) and ssh_port and not allowed_ssh_hosts - enabled: false - output: Disallowed SSH Connection (command=%proc.cmdline pid=%proc.pid connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id image=%container.image.repository) - priority: NOTICE - tags: [host, container, network, mitre_command_and_control, mitre_lateral_movement, T1021.004] - -# These rules and supporting macros are more of an example for how to -# use the fd.*ip and fd.*ip.name fields to match connection -# information against ips, netmasks, and complete domain names. -# -# To use this rule, you should enable it and -# populate allowed_{source,destination}_{ipaddrs,networks,domains} with the -# values that make sense for your environment. - -# Note that this can be either individual IPs or netmasks -- list: allowed_outbound_destination_ipaddrs - items: ['"127.0.0.1"', '"8.8.8.8"'] - -- list: allowed_outbound_destination_networks - items: ['"127.0.0.1/8"'] - -- list: allowed_outbound_destination_domains - items: [google.com, www.yahoo.com] - -- rule: Unexpected outbound connection destination - desc: Detect any outbound connection to a destination outside of an allowed set of ips, networks, or domain names - condition: > - outbound and not - ((fd.sip in (allowed_outbound_destination_ipaddrs)) or - (fd.snet in (allowed_outbound_destination_networks)) or - (fd.sip.name in (allowed_outbound_destination_domains))) - enabled: false - output: Disallowed outbound connection destination (command=%proc.cmdline pid=%proc.pid connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id image=%container.image.repository) - priority: NOTICE - tags: [host, container, network, mitre_command_and_control, TA0011] - -- list: allowed_inbound_source_ipaddrs - items: ['"127.0.0.1"'] - -- list: allowed_inbound_source_networks - items: ['"127.0.0.1/8"', '"10.0.0.0/8"'] - -- list: allowed_inbound_source_domains - items: [google.com] - -- rule: Unexpected inbound connection source - desc: Detect any inbound connection from a source outside of an allowed set of ips, networks, or domain names - condition: > - inbound and not - ((fd.cip in (allowed_inbound_source_ipaddrs)) or - (fd.cnet in (allowed_inbound_source_networks)) or - (fd.cip.name in (allowed_inbound_source_domains))) - enabled: false - output: Disallowed inbound connection source (command=%proc.cmdline pid=%proc.pid connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id image=%container.image.repository) - priority: NOTICE - tags: [host, container, network, mitre_command_and_control, TA0011] - -- list: bash_config_filenames - items: [.bashrc, .bash_profile, .bash_history, .bash_login, .bash_logout, .inputrc, .profile] - -- list: bash_config_files - items: [/etc/profile, /etc/bashrc] - -# Covers both csh and tcsh -- list: csh_config_filenames - items: [.cshrc, .login, .logout, .history, .tcshrc, .cshdirs] - -- list: csh_config_files - items: [/etc/csh.cshrc, /etc/csh.login] - -- list: zsh_config_filenames - items: [.zshenv, .zprofile, .zshrc, .zlogin, .zlogout] - -- list: shell_config_filenames - items: [bash_config_filenames, csh_config_filenames, zsh_config_filenames] - -- list: shell_config_files - items: [bash_config_files, csh_config_files] - -- list: shell_config_directories - items: [/etc/zsh] - -- macro: user_known_shell_config_modifiers - condition: (never_true) - -- rule: Modify Shell Configuration File - desc: Detect attempt to modify shell configuration files - condition: > - open_write and - (fd.filename in (shell_config_filenames) or - fd.name in (shell_config_files) or - fd.directory in (shell_config_directories)) - and not proc.name in (shell_binaries) - and not exe_running_docker_save - and not user_known_shell_config_modifiers - output: > - a shell configuration file has been modified (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid pcmdline=%proc.pcmdline file=%fd.name container_id=%container.id image=%container.image.repository) - priority: - WARNING - tags: [host, container, filesystem, mitre_persistence, T1546.004] - -# This rule is not enabled by default, as there are many legitimate -# readers of shell config files. - -- rule: Read Shell Configuration File - desc: Detect attempts to read shell configuration files by non-shell programs - condition: > - open_read and - (fd.filename in (shell_config_filenames) or - fd.name in (shell_config_files) or - fd.directory in (shell_config_directories)) and - (not proc.name in (shell_binaries)) - enabled: false - output: > - a shell configuration file was read by a non-shell program (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid file=%fd.name container_id=%container.id image=%container.image.repository) - priority: - WARNING - tags: [host, container, filesystem, mitre_discovery, T1546.004] - -# List of allowed container images that are known to execute binaries not part of their base image. -# Users can use this list to better tune the rule below (i.e reducing false positives) by considering their workloads, -# since this requires application specific knowledge. -- list: known_drop_and_execute_containers - items: [] - -- rule: Drop and execute new binary in container - desc: - Detect if an executable not belonging to the base image of a container is being executed. - The drop and execute pattern can be observed very often after an attacker gained an initial foothold. - is_exe_upper_layer filter field only applies for container runtimes that use overlayfs as union mount filesystem. - condition: > - spawned_process - and container - and proc.is_exe_upper_layer=true - and not container.image.repository in (known_drop_and_execute_containers) - output: > - Executing binary not part of base image (user=%user.name user_loginuid=%user.loginuid user_uid=%user.uid comm=%proc.cmdline exe=%proc.exe container_id=%container.id - image=%container.image.repository proc.name=%proc.name proc.sname=%proc.sname proc.pname=%proc.pname proc.aname[2]=%proc.aname[2] exe_flags=%evt.arg.flags - proc.exe_ino=%proc.exe_ino proc.exe_ino.ctime=%proc.exe_ino.ctime proc.exe_ino.mtime=%proc.exe_ino.mtime proc.exe_ino.ctime_duration_proc_start=%proc.exe_ino.ctime_duration_proc_start - proc.exepath=%proc.exepath proc.cwd=%proc.cwd proc.tty=%proc.tty container.start_ts=%container.start_ts proc.sid=%proc.sid proc.vpgid=%proc.vpgid evt.res=%evt.res) - priority: CRITICAL - tags: [container, mitre_persistence, TA0003] - -- macro: user_known_cron_jobs - condition: (never_true) - -- rule: Schedule Cron Jobs - desc: Detect cron jobs scheduled - condition: > - ((open_write and fd.name startswith /etc/cron) or - (spawned_process and proc.name = "crontab")) and - not user_known_cron_jobs - enabled: false - output: > - Cron jobs were scheduled to run (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid - file=%fd.name container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag) - priority: - NOTICE - tags: [host, container, filesystem, mitre_persistence, T1053.003] + condition: (proc.duration <= 5000000000) # Use this to test whether the event occurred within a container. - # When displaying container information in the output field, use # %container.info, without any leading term (file=%fd.name # %container.info user=%user.name user_loginuid=%user.loginuid, and not file=%fd.name @@ -547,12 +226,6 @@ - macro: container condition: (container.id != host) -- macro: container_started - condition: > - ((evt.type = container or - (spawned_process and proc.vpid=1)) and - container.image.repository != incomplete) - - macro: interactive condition: > ((proc.aname=sshd and proc.name != sshd) or @@ -569,35 +242,14 @@ - list: sshkit_script_binaries items: [10_etc_sudoers., 10_passwd_group] -- list: plesk_binaries - items: [sw-engine, sw-engine-fpm, sw-engine-kv, filemng, f2bmng] - # System users that should never log into a system. Consider adding your own # service users (e.g. 'apache' or 'mysqld') here. - macro: system_users - condition: user.name in (bin, daemon, games, lp, mail, nobody, sshd, sync, uucp, www-data) - -- macro: httpd_writing_ssl_conf - condition: > - (proc.pname=run-httpd and - (proc.cmdline startswith "sed -ri" or proc.cmdline startswith "sed -i") and - (fd.name startswith /etc/httpd/conf.d/ or fd.name startswith /etc/httpd/conf)) - -- macro: userhelper_writing_etc_security - condition: (proc.name=userhelper and fd.name startswith /etc/security) + condition: (user.name in (bin, daemon, games, lp, mail, nobody, sshd, sync, uucp, www-data)) - macro: ansible_running_python condition: (proc.name in (python, pypy, python3) and proc.cmdline contains ansible) -- macro: python_running_chef - condition: (proc.name=python and (proc.cmdline contains yum-dump.py or proc.cmdline="python /usr/bin/chef-monitor.py")) - -- macro: python_running_denyhosts - condition: > - (proc.name=python and - (proc.cmdline contains /usr/sbin/denyhosts or - proc.cmdline contains /usr/local/bin/denyhosts.py)) - # Qualys seems to run a variety of shell subprocesses, at various # levels. This checks at a few levels without the cost of a full # proc.aname, which traverses the full parent hierarchy. @@ -608,21 +260,6 @@ proc.aname[3]=qualys-cloud-ag or proc.aname[4]=qualys-cloud-ag) -- macro: run_by_sumologic_securefiles - condition: > - ((proc.cmdline="usermod -a -G sumologic_collector" or - proc.cmdline="groupadd sumologic_collector") and - (proc.pname=secureFiles.sh and proc.aname[2]=java)) - -- macro: run_by_yum - condition: ((proc.pname=sh and proc.aname[2]=yum) or - (proc.aname[2]=sh and proc.aname[3]=yum)) - -- macro: run_by_ms_oms - condition: > - (proc.aname[3] startswith omsagent- or - proc.aname[3] startswith scx-) - - macro: run_by_google_accounts_daemon condition: > (proc.aname[1] startswith google_accounts or @@ -635,12 +272,6 @@ proc.aname[2]=chef-client or proc.aname[3]=chef-client or proc.name=chef-client) -- macro: run_by_adclient - condition: (proc.aname[2]=adclient or proc.aname[3]=adclient or proc.aname[4]=adclient) - -- macro: run_by_centrify - condition: (proc.aname[2]=centrify or proc.aname[3]=centrify or proc.aname[4]=centrify) - # Also handles running semi-indirectly via scl - macro: run_by_foreman condition: > @@ -648,40 +279,12 @@ ((proc.pname in (rake, ruby, scl) and proc.aname[5] in (tfm-rake,tfm-ruby)) or (proc.pname=scl and proc.aname[2] in (tfm-rake,tfm-ruby)))) -- macro: java_running_sdjagent - condition: proc.name=java and proc.cmdline contains sdjagent.jar - -- macro: kubelet_running_loopback - condition: (proc.pname=kubelet and proc.name=loopback) - - macro: python_mesos_marathon_scripting condition: (proc.pcmdline startswith "python3 /marathon-lb/marathon_lb.py") - macro: splunk_running_forwarder condition: (proc.pname=splunkd and proc.cmdline startswith "sh -c /opt/splunkforwarder") -- macro: parent_supervise_running_multilog - condition: (proc.name=multilog and proc.pname=supervise) - -- macro: supervise_writing_status - condition: (proc.name in (supervise,svc) and fd.name startswith "/etc/sb/") - -- macro: pki_realm_writing_realms - condition: (proc.cmdline startswith "bash /usr/local/lib/pki/pki-realm" and fd.name startswith /etc/pki/realms) - -- macro: htpasswd_writing_passwd - condition: (proc.name=htpasswd and fd.name=/etc/nginx/.htpasswd) - -- macro: lvprogs_writing_conf - condition: > - (proc.name in (dmeventd,lvcreate,pvscan,lvs) and - (fd.name startswith /etc/lvm/archive or - fd.name startswith /etc/lvm/backup or - fd.name startswith /etc/lvm/cache)) - -- macro: ovsdb_writing_openvswitch - condition: (proc.name=ovsdb-server and fd.directory=/etc/openvswitch) - - macro: perl_running_plesk condition: (proc.cmdline startswith "perl /opt/psa/admin/bin/plesk_agent_manager" or proc.pcmdline startswith "perl /opt/psa/admin/bin/plesk_agent_manager") @@ -702,733 +305,60 @@ (fd.name=/etc/shadow or fd.directory=/etc/pam.d)) -- macro: parent_ucf_writing_conf - condition: (proc.pname=ucf and proc.aname[2]=frontend) - -- macro: consul_template_writing_conf - condition: > - ((proc.name=consul-template and fd.name startswith /etc/haproxy) or - (proc.name=reload.sh and proc.aname[2]=consul-template and fd.name startswith /etc/ssl)) - -- macro: countly_writing_nginx_conf - condition: (proc.cmdline startswith "nodejs /opt/countly/bin" and fd.name startswith /etc/nginx) - -- list: ms_oms_binaries - items: [omi.postinst, omsconfig.posti, scx.postinst, omsadmin.sh, omiagent] - -- macro: ms_oms_writing_conf - condition: > - ((proc.name in (omiagent,omsagent,in_heartbeat_r*,omsadmin.sh,PerformInventor,dsc_host) - or proc.pname in (ms_oms_binaries) - or proc.aname[2] in (ms_oms_binaries)) - and (fd.name startswith /etc/opt/omi or fd.name startswith /etc/opt/microsoft/omsagent)) - -- macro: ms_scx_writing_conf - condition: (proc.name in (GetLinuxOS.sh) and fd.name startswith /etc/opt/microsoft/scx) - -- macro: azure_scripts_writing_conf - condition: (proc.pname startswith "bash /var/lib/waagent/" and fd.name startswith /etc/azure) - -- macro: azure_networkwatcher_writing_conf - condition: (proc.name in (NetworkWatcherA) and fd.name=/etc/init.d/AzureNetworkWatcherAgent) - -- macro: couchdb_writing_conf - condition: (proc.name=beam.smp and proc.cmdline contains couchdb and fd.name startswith /etc/couchdb) - -- macro: update_texmf_writing_conf - condition: (proc.name=update-texmf and fd.name startswith /etc/texmf) - -- macro: slapadd_writing_conf - condition: (proc.name=slapadd and fd.name startswith /etc/ldap) - -- macro: openldap_writing_conf - condition: (proc.pname=run-openldap.sh and fd.name startswith /etc/openldap) - -- macro: ucpagent_writing_conf - condition: (proc.name=apiserver and container.image.repository=docker/ucp-agent and fd.name=/etc/authorization_config.cfg) - -- macro: iscsi_writing_conf - condition: (proc.name=iscsiadm and fd.name startswith /etc/iscsi) - -- macro: istio_writing_conf - condition: (proc.name=pilot-agent and fd.name startswith /etc/istio) - -- macro: symantec_writing_conf - condition: > - ((proc.name=symcfgd and fd.name startswith /etc/symantec) or - (proc.name=navdefutil and fd.name=/etc/symc-defutils.conf)) - -- macro: liveupdate_writing_conf - condition: (proc.cmdline startswith "java LiveUpdate" and fd.name in (/etc/liveupdate.conf, /etc/Product.Catalog.JavaLiveUpdate)) - -- macro: rancher_agent - condition: (proc.name=agent and container.image.repository contains "rancher/agent") - -- macro: rancher_network_manager - condition: (proc.name=rancher-bridge and container.image.repository contains "rancher/network-manager") - -- macro: sosreport_writing_files - condition: > - (proc.name=urlgrabber-ext- and proc.aname[3]=sosreport and - (fd.name startswith /etc/pkt/nssdb or fd.name startswith /etc/pki/nssdb)) - -- macro: pkgmgmt_progs_writing_pki - condition: > - (proc.name=urlgrabber-ext- and proc.pname in (yum, yum-cron, repoquery) and - (fd.name startswith /etc/pkt/nssdb or fd.name startswith /etc/pki/nssdb)) - -- macro: update_ca_trust_writing_pki - condition: (proc.pname=update-ca-trust and proc.name=trust and fd.name startswith /etc/pki) - -- macro: brandbot_writing_os_release - condition: proc.name=brandbot and fd.name=/etc/os-release - -- macro: selinux_writing_conf - condition: (proc.name in (semodule,genhomedircon,sefcontext_comp) and fd.name startswith /etc/selinux) - -- list: veritas_binaries - items: [vxconfigd, sfcache, vxclustadm, vxdctl, vxprint, vxdmpadm, vxdisk, vxdg, vxassist, vxtune] - - macro: veritas_driver_script condition: (proc.cmdline startswith "perl /opt/VRTSsfmh/bin/mh_driver.pl") -- macro: veritas_progs - condition: (proc.name in (veritas_binaries) or veritas_driver_script) - -- macro: veritas_writing_config - condition: (veritas_progs and (fd.name startswith /etc/vx or fd.name startswith /etc/opt/VRTS or fd.name startswith /etc/vom)) - -- macro: nginx_writing_conf - condition: (proc.name in (nginx,nginx-ingress-c,nginx-ingress) and (fd.name startswith /etc/nginx or fd.name startswith /etc/ingress-controller)) - -- macro: nginx_writing_certs - condition: > - (((proc.name=openssl and proc.pname=nginx-launch.sh) or proc.name=nginx-launch.sh) and fd.name startswith /etc/nginx/certs) - -- macro: chef_client_writing_conf - condition: (proc.pcmdline startswith "chef-client /opt/gitlab" and fd.name startswith /etc/gitlab) - -- macro: centrify_writing_krb - condition: (proc.name in (adjoin,addns) and fd.name startswith /etc/krb5) - -- macro: sssd_writing_krb - condition: (proc.name=adcli and proc.aname[2]=sssd and fd.name startswith /etc/krb5) - -- macro: cockpit_writing_conf - condition: > - ((proc.pname=cockpit-kube-la or proc.aname[2]=cockpit-kube-la) - and fd.name startswith /etc/cockpit) - -- macro: ipsec_writing_conf - condition: (proc.name=start-ipsec.sh and fd.directory=/etc/ipsec) - -- macro: exe_running_docker_save - condition: > - proc.name = "exe" - and (proc.cmdline contains "/var/lib/docker" - or proc.cmdline contains "/var/run/docker") - and proc.pname in (dockerd, docker, dockerd-current, docker-current) - -# Ideally we'd have a length check here as well but -# filterchecks don't have operators like len() -- macro: sed_temporary_file - condition: (proc.name=sed and fd.name startswith "/etc/sed") - -- macro: python_running_get_pip - condition: (proc.cmdline startswith "python get-pip.py") - -- macro: python_running_ms_oms - condition: (proc.cmdline startswith "python /var/lib/waagent/") - -- macro: gugent_writing_guestagent_log - condition: (proc.name=gugent and fd.name=GuestAgent.log) - -- macro: dse_writing_tmp - condition: (proc.name=dse-entrypoint and fd.name=/root/tmp__) - -- macro: zap_writing_state - condition: (proc.name=java and proc.cmdline contains "jar /zap" and fd.name startswith /root/.ZAP) - -- macro: airflow_writing_state - condition: (proc.name=airflow and fd.name startswith /root/airflow) - -- macro: rpm_writing_root_rpmdb - condition: (proc.name=rpm and fd.directory=/root/.rpmdb) - -- macro: maven_writing_groovy - condition: (proc.name=java and proc.cmdline contains "classpath /usr/local/apache-maven" and fd.name startswith /root/.groovy) - -- macro: chef_writing_conf - condition: (proc.name=chef-client and fd.name startswith /root/.chef) - -- macro: kubectl_writing_state - condition: (proc.name in (kubectl,oc) and fd.name startswith /root/.kube) - -- macro: java_running_cassandra - condition: (proc.name=java and proc.cmdline contains "cassandra.jar") - -- macro: cassandra_writing_state - condition: (java_running_cassandra and fd.directory=/root/.cassandra) - -# Istio -- macro: galley_writing_state - condition: (proc.name=galley and fd.name in (known_istio_files)) - -- list: known_istio_files - items: [/healthready, /healthliveness] - -- macro: calico_writing_state - condition: (proc.name=kube-controller and fd.name startswith /status.json and k8s.pod.name startswith calico) - -- macro: calico_writing_envvars - condition: (proc.name=start_runit and fd.name startswith "/etc/envvars" and container.image.repository endswith "calico/node") - -- list: repository_files - items: [sources.list] - -- list: repository_directories - items: [/etc/apt/sources.list.d, /etc/yum.repos.d, /etc/apt] - -- macro: access_repositories - condition: (fd.directory in (repository_directories) or - (fd.name pmatch (repository_directories) and - fd.filename in (repository_files))) - -- macro: modify_repositories - condition: (evt.arg.newpath pmatch (repository_directories)) - -- macro: user_known_update_package_registry - condition: (never_true) - -- rule: Update Package Repository - desc: Detect package repositories get updated - condition: > - ((open_write and access_repositories) or (modify and modify_repositories)) - and not package_mgmt_procs - and not package_mgmt_ancestor_procs - and not exe_running_docker_save - and not user_known_update_package_registry - output: > - Repository files get updated (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid pcmdline=%proc.pcmdline file=%fd.name newpath=%evt.arg.newpath container_id=%container.id image=%container.image.repository) - priority: - NOTICE - tags: [host, container, filesystem, mitre_persistence, T1072] - -# Users should overwrite this macro to specify conditions under which a -# write under the binary dir is ignored. For example, it may be okay to -# install a binary in the context of a ci/cd build. -- macro: user_known_write_below_binary_dir_activities - condition: (never_true) - -- rule: Write below binary dir - desc: an attempt to write to any file below a set of binary directories - condition: > - bin_dir and evt.dir = < and open_write - and not package_mgmt_procs - and not exe_running_docker_save - and not python_running_get_pip - and not python_running_ms_oms - and not user_known_write_below_binary_dir_activities - output: > - File below a known binary directory opened for writing (user=%user.name user_loginuid=%user.loginuid - command=%proc.cmdline pid=%proc.pid file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline gparent=%proc.aname[2] container_id=%container.id image=%container.image.repository) - priority: ERROR - tags: [host, container, filesystem, mitre_persistence, T1543] - -# If you'd like to generally monitor a wider set of directories on top -# of the ones covered by the rule Write below binary dir, you can use -# the following rule and lists. - -- list: monitored_directories - items: [/boot, /lib, /lib64, /usr/lib, /usr/local/lib, /usr/local/sbin, /usr/local/bin, /root/.ssh] - - macro: user_ssh_directory condition: (fd.name contains '/.ssh/' and fd.name glob '/home/*/.ssh/*') - macro: directory_traversal condition: (fd.nameraw contains '../' and fd.nameraw glob '*../*../*') -# google_accounts_(daemon) -- macro: google_accounts_daemon_writing_ssh - condition: (proc.name=google_accounts and user_ssh_directory) - -- macro: cloud_init_writing_ssh - condition: (proc.name=cloud-init and user_ssh_directory) - -- macro: mkinitramfs_writing_boot - condition: (proc.pname in (mkinitramfs, update-initramf) and fd.directory=/boot) - -- macro: monitored_dir - condition: > - (fd.directory in (monitored_directories) - or user_ssh_directory) - and not mkinitramfs_writing_boot - -# Add conditions to this macro (probably in a separate file, -# overwriting this macro) to allow for specific combinations of -# programs writing below monitored directories. -# -# Its default value is an expression that always is false, which -# becomes true when the "not ..." in the rule is applied. -- macro: user_known_write_monitored_dir_conditions - condition: (never_true) - -- rule: Write below monitored dir - desc: an attempt to write to any file below a set of monitored directories - condition: > - evt.dir = < and open_write and monitored_dir - and not package_mgmt_procs - and not coreos_write_ssh_dir - and not exe_running_docker_save - and not python_running_get_pip - and not python_running_ms_oms - and not google_accounts_daemon_writing_ssh - and not cloud_init_writing_ssh - and not user_known_write_monitored_dir_conditions - output: > - File below a monitored directory opened for writing (user=%user.name user_loginuid=%user.loginuid - command=%proc.cmdline pid=%proc.pid file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline gparent=%proc.aname[2] container_id=%container.id image=%container.image.repository) - priority: ERROR - tags: [host, container, filesystem, mitre_persistence, T1543] - # ****************************************************************************** # * "Directory traversal monitored file read" requires FALCO_ENGINE_VERSION 13 * # ****************************************************************************** - - rule: Directory traversal monitored file read desc: > - Web applications can be vulnerable to directory traversal attacks that allow accessing files outside of the web app's root directory (e.g. Arbitrary File Read bugs). - System directories like /etc are typically accessed via absolute paths. Access patterns outside of this (here path traversal) can be regarded as suspicious. - This rule includes failed file open attempts. - condition: (open_read or open_file_failed) and (etc_dir or user_ssh_directory or fd.name startswith /root/.ssh or fd.name contains "id_rsa") and directory_traversal and not proc.pname in (shell_binaries) + Web applications can be vulnerable to directory traversal attacks that allow accessing files outside of the web app's root directory + (e.g. Arbitrary File Read bugs). System directories like /etc are typically accessed via absolute paths. Access patterns outside of this + (here path traversal) can be regarded as suspicious. This rule includes failed file open attempts. + condition: > + (open_read or open_file_failed) + and (etc_dir or user_ssh_directory or + fd.name startswith /root/.ssh or + fd.name contains "id_rsa") + and directory_traversal + and not proc.pname in (shell_binaries) enabled: true - output: > - Read monitored file via directory traversal (username=%user.name useruid=%user.uid user_loginuid=%user.loginuid program=%proc.name exe=%proc.exepath - command=%proc.cmdline pid=%proc.pid parent=%proc.pname file=%fd.name fileraw=%fd.nameraw parent=%proc.pname - gparent=%proc.aname[2] container_id=%container.id image=%container.image.repository returncode=%evt.res cwd=%proc.cwd) + output: Read monitored file via directory traversal (file=%fd.name fileraw=%fd.nameraw gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) priority: WARNING - tags: [host, container, filesystem, mitre_discovery, mitre_exfiltration, mitre_credential_access, T1555, T1212, T1020, T1552, T1083] + tags: [maturity_stable, host, container, filesystem, mitre_credential_access, T1555] -# This rule is disabled by default as many system management tools -# like ansible, etc can read these files/paths. Enable it using this macro. +- macro: cmp_cp_by_passwd + condition: (proc.name in (cmp, cp) and proc.pname in (passwd, run-parts)) -- macro: user_known_read_ssh_information_activities - condition: (never_true) - -- rule: Read ssh information - desc: Any attempt to read files below ssh directories by non-ssh programs - condition: > - ((open_read or open_directory) and - (user_ssh_directory or fd.name startswith /root/.ssh) and - not user_known_read_ssh_information_activities and - not proc.name in (ssh_binaries)) - enabled: false - output: > - ssh-related file/directory read by non-ssh program (user=%user.name user_loginuid=%user.loginuid - command=%proc.cmdline pid=%proc.pid file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline container_id=%container.id image=%container.image.repository) - priority: ERROR - tags: [host, container, filesystem, mitre_discovery, T1005] - -- list: safe_etc_dirs - items: [/etc/cassandra, /etc/ssl/certs/java, /etc/logstash, /etc/nginx/conf.d, /etc/container_environment, /etc/hrmconfig, /etc/fluent/configs.d] - -- macro: fluentd_writing_conf_files - condition: (proc.name=start-fluentd and fd.name in (/etc/fluent/fluent.conf, /etc/td-agent/td-agent.conf)) - -- macro: qualys_writing_conf_files - condition: (proc.name=qualys-cloud-ag and fd.name=/etc/qualys/cloud-agent/qagent-log.conf) - -- macro: git_writing_nssdb - condition: (proc.name=git-remote-http and fd.directory=/etc/pki/nssdb) - -- macro: plesk_writing_keys - condition: (proc.name in (plesk_binaries) and fd.name startswith /etc/sw/keys) - -- macro: plesk_install_writing_apache_conf - condition: (proc.cmdline startswith "bash -hB /usr/lib/plesk-9.0/services/webserver.apache configure" - and fd.name="/etc/apache2/apache2.conf.tmp") - -- macro: plesk_running_mktemp - condition: (proc.name=mktemp and proc.aname[3] in (plesk_binaries)) - -- macro: networkmanager_writing_resolv_conf - condition: proc.aname[2]=nm-dispatcher and fd.name=/etc/resolv.conf - -- macro: add_shell_writing_shells_tmp - condition: (proc.name=add-shell and fd.name=/etc/shells.tmp) - -- macro: duply_writing_exclude_files - condition: (proc.name=touch and proc.pcmdline startswith "bash /usr/bin/duply" and fd.name startswith "/etc/duply") - -- macro: xmlcatalog_writing_files - condition: (proc.name=update-xmlcatal and fd.directory=/etc/xml) - -- macro: datadog_writing_conf - condition: ((proc.cmdline startswith "python /opt/datadog-agent" or - proc.cmdline startswith "entrypoint.sh /entrypoint.sh datadog start" or - proc.cmdline startswith "agent.py /opt/datadog-agent") - and fd.name startswith "/etc/dd-agent") - -- macro: rancher_writing_conf - condition: ((proc.name in (healthcheck, lb-controller, rancher-dns)) and - (container.image.repository contains "rancher/healthcheck" or - container.image.repository contains "rancher/lb-service-haproxy" or - container.image.repository contains "rancher/dns") and - (fd.name startswith "/etc/haproxy" or fd.name startswith "/etc/rancher-dns")) - -- macro: rancher_writing_root - condition: (proc.name=rancher-metadat and - (container.image.repository contains "rancher/metadata" or container.image.repository contains "rancher/lb-service-haproxy") and - fd.name startswith "/answers.json") - -- macro: checkpoint_writing_state - condition: (proc.name=checkpoint and - container.image.repository contains "coreos/pod-checkpointer" and - fd.name startswith "/etc/kubernetes") - -- macro: jboss_in_container_writing_passwd - condition: > - ((proc.cmdline="run-java.sh /opt/jboss/container/java/run/run-java.sh" - or proc.cmdline="run-java.sh /opt/run-java/run-java.sh") - and container - and fd.name=/etc/passwd) - -- macro: curl_writing_pki_db - condition: (proc.name=curl and fd.directory=/etc/pki/nssdb) - -- macro: haproxy_writing_conf - condition: ((proc.name in (update-haproxy-,haproxy_reload.) or proc.pname in (update-haproxy-,haproxy_reload,haproxy_reload.)) - and (fd.name=/etc/openvpn/client.map or fd.name startswith /etc/haproxy)) - -- macro: java_writing_conf - condition: (proc.name=java and fd.name=/etc/.java/.systemPrefs/.system.lock) - -- macro: rabbitmq_writing_conf - condition: (proc.name=rabbitmq-server and fd.directory=/etc/rabbitmq) - -- macro: rook_writing_conf - condition: (proc.name=toolbox.sh and container.image.repository=rook/toolbox - and fd.directory=/etc/ceph) - -- macro: httpd_writing_conf_logs - condition: (proc.name=httpd and fd.name startswith /etc/httpd/) - -- macro: mysql_writing_conf - condition: > - ((proc.name in (start-mysql.sh, run-mysqld) or proc.pname=start-mysql.sh) and - (fd.name startswith /etc/mysql or fd.directory=/etc/my.cnf.d)) - -- macro: redis_writing_conf - condition: > - (proc.name in (run-redis, redis-launcher.) and (fd.name=/etc/redis.conf or fd.name startswith /etc/redis)) - -- macro: openvpn_writing_conf - condition: (proc.name in (openvpn,openvpn-entrypo) and fd.name startswith /etc/openvpn) - -- macro: php_handlers_writing_conf - condition: (proc.name=php_handlers_co and fd.name=/etc/psa/php_versions.json) - -- macro: sed_writing_temp_file - condition: > - ((proc.aname[3]=cron_start.sh and fd.name startswith /etc/security/sed) or - (proc.name=sed and (fd.name startswith /etc/apt/sources.list.d/sed or - fd.name startswith /etc/apt/sed or - fd.name startswith /etc/apt/apt.conf.d/sed))) - -- macro: cron_start_writing_pam_env - condition: (proc.cmdline="bash /usr/sbin/start-cron" and fd.name=/etc/security/pam_env.conf) - -# In some cases dpkg-reconfigur runs commands that modify /etc. Not -# putting the full set of package management programs yet. -- macro: dpkg_scripting - condition: (proc.aname[2] in (dpkg-reconfigur, dpkg-preconfigu)) - -- macro: ufw_writing_conf - condition: (proc.name=ufw and fd.directory=/etc/ufw) - -- macro: calico_writing_conf - condition: > - (((proc.name = calico-node) or - (container.image.repository=gcr.io/projectcalico-org/node and proc.name in (start_runit, cp)) or - (container.image.repository=gcr.io/projectcalico-org/cni and proc.name=sed)) - and fd.name startswith /etc/calico) - -- macro: prometheus_conf_writing_conf - condition: (proc.name=prometheus-conf and fd.name startswith /etc/prometheus/config_out) - -- macro: openshift_writing_conf - condition: (proc.name=oc and fd.name startswith /etc/origin/node) - -- macro: keepalived_writing_conf - condition: (proc.name in (keepalived, kube-keepalived) and fd.name=/etc/keepalived/keepalived.conf) - -- macro: etcd_manager_updating_dns - condition: (container and proc.name=etcd-manager and fd.name=/etc/hosts) - -- macro: automount_using_mtab - condition: (proc.pname = automount and fd.name startswith /etc/mtab) - -- macro: mcafee_writing_cma_d - condition: (proc.name=macompatsvc and fd.directory=/etc/cma.d) - -- macro: avinetworks_supervisor_writing_ssh - condition: > - (proc.cmdline="se_supervisor.p /opt/avi/scripts/se_supervisor.py -d" and - (fd.name startswith /etc/ssh/known_host_ or - fd.name startswith /etc/ssh/ssh_monitor_config_ or - fd.name startswith /etc/ssh/ssh_config_)) - -- macro: multipath_writing_conf - condition: (proc.name = multipath and fd.name startswith /etc/multipath/) - -# Add conditions to this macro (probably in a separate file, -# overwriting this macro) to allow for specific combinations of -# programs writing below specific directories below -# /etc. fluentd_writing_conf_files is a good example to follow, as it -# specifies both the program doing the writing as well as the specific -# files it is allowed to modify. -# -# In this file, it just takes one of the programs in the base macro -# and repeats it. - -- macro: user_known_write_etc_conditions - condition: proc.name=confd - -# This is a placeholder for user to extend the whitelist for write below etc rule -- macro: user_known_write_below_etc_activities - condition: (never_true) - -- macro: calico_node - condition: (container.image.repository endswith calico/node and proc.name=calico-node) - -- macro: write_etc_common - condition: > - etc_dir and evt.dir = < and open_write - and proc_name_exists - and not proc.name in (passwd_binaries, shadowutils_binaries, sysdigcloud_binaries, - package_mgmt_binaries, ssl_mgmt_binaries, dhcp_binaries, - dev_creation_binaries, shell_mgmt_binaries, - mail_config_binaries, - sshkit_script_binaries, - ldconfig.real, ldconfig, confd, gpg, insserv, - apparmor_parser, update-mime, tzdata.config, tzdata.postinst, - systemd, systemd-machine, systemd-sysuser, - debconf-show, rollerd, bind9.postinst, sv, - gen_resolvconf., update-ca-certi, certbot, runsv, - qualys-cloud-ag, locales.postins, nomachine_binaries, - adclient, certutil, crlutil, pam-auth-update, parallels_insta, - openshift-launc, update-rc.d, puppet) - and not (container and proc.cmdline in ("cp /run/secrets/kubernetes.io/serviceaccount/ca.crt /etc/pki/ca-trust/source/anchors/openshift-ca.crt")) - and not proc.pname in (sysdigcloud_binaries, mail_config_binaries, hddtemp.postins, sshkit_script_binaries, locales.postins, deb_binaries, dhcp_binaries) - and not fd.name pmatch (safe_etc_dirs) - and not fd.name in (/etc/container_environment.sh, /etc/container_environment.json, /etc/motd, /etc/motd.svc) - and not sed_temporary_file - and not exe_running_docker_save - and not ansible_running_python - and not python_running_denyhosts - and not fluentd_writing_conf_files - and not user_known_write_etc_conditions - and not run_by_centrify - and not run_by_adclient - and not qualys_writing_conf_files - and not git_writing_nssdb - and not plesk_writing_keys - and not plesk_install_writing_apache_conf - and not plesk_running_mktemp - and not networkmanager_writing_resolv_conf - and not run_by_chef - and not add_shell_writing_shells_tmp - and not duply_writing_exclude_files - and not xmlcatalog_writing_files - and not parent_supervise_running_multilog - and not supervise_writing_status - and not pki_realm_writing_realms - and not htpasswd_writing_passwd - and not lvprogs_writing_conf - and not ovsdb_writing_openvswitch - and not datadog_writing_conf - and not curl_writing_pki_db - and not haproxy_writing_conf - and not java_writing_conf - and not dpkg_scripting - and not parent_ucf_writing_conf - and not rabbitmq_writing_conf - and not rook_writing_conf - and not php_handlers_writing_conf - and not sed_writing_temp_file - and not cron_start_writing_pam_env - and not httpd_writing_conf_logs - and not mysql_writing_conf - and not openvpn_writing_conf - and not consul_template_writing_conf - and not countly_writing_nginx_conf - and not ms_oms_writing_conf - and not ms_scx_writing_conf - and not azure_scripts_writing_conf - and not azure_networkwatcher_writing_conf - and not couchdb_writing_conf - and not update_texmf_writing_conf - and not slapadd_writing_conf - and not symantec_writing_conf - and not liveupdate_writing_conf - and not sosreport_writing_files - and not selinux_writing_conf - and not veritas_writing_config - and not nginx_writing_conf - and not nginx_writing_certs - and not chef_client_writing_conf - and not centrify_writing_krb - and not sssd_writing_krb - and not cockpit_writing_conf - and not ipsec_writing_conf - and not httpd_writing_ssl_conf - and not userhelper_writing_etc_security - and not pkgmgmt_progs_writing_pki - and not update_ca_trust_writing_pki - and not brandbot_writing_os_release - and not redis_writing_conf - and not openldap_writing_conf - and not ucpagent_writing_conf - and not iscsi_writing_conf - and not istio_writing_conf - and not ufw_writing_conf - and not calico_writing_conf - and not calico_writing_envvars - and not prometheus_conf_writing_conf - and not openshift_writing_conf - and not keepalived_writing_conf - and not rancher_writing_conf - and not checkpoint_writing_state - and not jboss_in_container_writing_passwd - and not etcd_manager_updating_dns - and not user_known_write_below_etc_activities - and not automount_using_mtab - and not mcafee_writing_cma_d - and not avinetworks_supervisor_writing_ssh - and not multipath_writing_conf - and not calico_node - -- rule: Write below etc - desc: an attempt to write to any file below /etc - condition: write_etc_common - output: "File below /etc opened for writing (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid parent=%proc.pname pcmdline=%proc.pcmdline file=%fd.name program=%proc.name gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] container_id=%container.id image=%container.image.repository)" - priority: ERROR - tags: [host, container, filesystem, mitre_persistence, T1098] - -- list: known_root_files - items: [/root/.monit.state, /root/.auth_tokens, /root/.bash_history, /root/.ash_history, /root/.aws/credentials, - /root/.viminfo.tmp, /root/.lesshst, /root/.bzr.log, /root/.gitconfig.lock, /root/.babel.json, /root/.localstack, - /root/.node_repl_history, /root/.mongorc.js, /root/.dbshell, /root/.augeas/history, /root/.rnd, /root/.wget-hsts, /health, /exec.fifo] - -- list: known_root_directories - items: [/root/.oracle_jre_usage, /root/.ssh, /root/.subversion, /root/.nami] - -- macro: known_root_conditions - condition: (fd.name startswith /root/orcexec. - or fd.name startswith /root/.m2 - or fd.name startswith /root/.npm - or fd.name startswith /root/.pki - or fd.name startswith /root/.ivy2 - or fd.name startswith /root/.config/Cypress - or fd.name startswith /root/.config/pulse - or fd.name startswith /root/.config/configstore - or fd.name startswith /root/jenkins/workspace - or fd.name startswith /root/.jenkins - or fd.name startswith /root/.cache - or fd.name startswith /root/.sbt - or fd.name startswith /root/.java - or fd.name startswith /root/.glide - or fd.name startswith /root/.sonar - or fd.name startswith /root/.v8flag - or fd.name startswith /root/infaagent - or fd.name startswith /root/.local/lib/python - or fd.name startswith /root/.pm2 - or fd.name startswith /root/.gnupg - or fd.name startswith /root/.pgpass - or fd.name startswith /root/.theano - or fd.name startswith /root/.gradle - or fd.name startswith /root/.android - or fd.name startswith /root/.ansible - or fd.name startswith /root/.crashlytics - or fd.name startswith /root/.dbus - or fd.name startswith /root/.composer - or fd.name startswith /root/.gconf - or fd.name startswith /root/.nv - or fd.name startswith /root/.local/share/jupyter - or fd.name startswith /root/oradiag_root - or fd.name startswith /root/workspace - or fd.name startswith /root/jvm - or fd.name startswith /root/.node-gyp) - -# Add conditions to this macro (probably in a separate file, -# overwriting this macro) to allow for specific combinations of -# programs writing below specific directories below -# / or /root. -# -# In this file, it just takes one of the condition in the base macro -# and repeats it. -- macro: user_known_write_root_conditions - condition: fd.name=/root/.bash_history - -# This is a placeholder for user to extend the whitelist for write below root rule -- macro: user_known_write_below_root_activities - condition: (never_true) - -- macro: runc_writing_exec_fifo - condition: (proc.cmdline="runc:[1:CHILD] init" and fd.name=/exec.fifo) - -- macro: runc_writing_var_lib_docker - condition: (proc.cmdline="runc:[1:CHILD] init" and evt.arg.filename startswith /var/lib/docker) - -- macro: mysqlsh_writing_state - condition: (proc.name=mysqlsh and fd.directory=/root/.mysqlsh) - -- rule: Write below root - desc: an attempt to write to any file directly below / or /root - condition: > - root_dir and evt.dir = < and open_write - and proc_name_exists - and not fd.name in (known_root_files) - and not fd.directory pmatch (known_root_directories) - and not exe_running_docker_save - and not gugent_writing_guestagent_log - and not dse_writing_tmp - and not zap_writing_state - and not airflow_writing_state - and not rpm_writing_root_rpmdb - and not maven_writing_groovy - and not chef_writing_conf - and not kubectl_writing_state - and not cassandra_writing_state - and not galley_writing_state - and not calico_writing_state - and not rancher_writing_root - and not runc_writing_exec_fifo - and not mysqlsh_writing_state - and not known_root_conditions - and not user_known_write_root_conditions - and not user_known_write_below_root_activities - output: "File below / or /root opened for writing (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid parent=%proc.pname file=%fd.name program=%proc.name container_id=%container.id image=%container.image.repository)" - priority: ERROR - tags: [host, container, filesystem, mitre_persistence, TA0003] - -- macro: cmp_cp_by_passwd - condition: proc.name in (cmp, cp) and proc.pname in (passwd, run-parts) - -- macro: user_known_read_sensitive_files_activities +- macro: user_known_read_sensitive_files_activities condition: (never_true) - rule: Read sensitive file trusted after startup desc: > - an attempt to read any sensitive file (e.g. files containing user/password/authentication + An attempt to read any sensitive file (e.g. files containing user/password/authentication information) by a trusted program after startup. Trusted programs might read these files - at startup to load initial state, but not afterwards. - condition: sensitive_files and open_read and server_procs and not proc_is_new and proc.name!="sshd" and not user_known_read_sensitive_files_activities - output: > - Sensitive file opened for reading by trusted program after startup (user=%user.name user_loginuid=%user.loginuid - command=%proc.cmdline pid=%proc.pid parent=%proc.pname file=%fd.name parent=%proc.pname gparent=%proc.aname[2] container_id=%container.id image=%container.image.repository) + at startup to load initial state, but not afterwards. Can be customized as needed. + In modern containerized cloud infrastructures, accessing traditional Linux sensitive files + might be less relevant, yet it remains valuable for baseline detections. While we provide additional + rules for SSH or cloud vendor-specific credentials, you can significantly enhance your security + program by crafting custom rules for critical application credentials unique to your environment. + condition: > + open_read + and sensitive_files + and server_procs + and not proc_is_new + and proc.name!="sshd" + and not user_known_read_sensitive_files_activities + output: Sensitive file opened for reading by trusted program after startup (file=%fd.name pcmdline=%proc.pcmdline gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) priority: WARNING - tags: [host, container, filesystem, mitre_credential_access, T1555, T1212, T1020, T1552, T1083] + tags: [maturity_stable, host, container, filesystem, mitre_credential_access, T1555] - list: read_sensitive_file_binaries items: [ @@ -1447,7 +377,6 @@ # # In this file, it just takes one of the macros in the base rule # and repeats it. - - macro: user_read_sensitive_file_conditions condition: cmp_cp_by_passwd @@ -1468,10 +397,15 @@ - rule: Read sensitive file untrusted desc: > - an attempt to read any sensitive file (e.g. files containing user/password/authentication - information). Exceptions are made for known trusted programs. - condition: > - sensitive_files and open_read + An attempt to read any sensitive file (e.g. files containing user/password/authentication + information). Exceptions are made for known trusted programs. Can be customized as needed. + In modern containerized cloud infrastructures, accessing traditional Linux sensitive files + might be less relevant, yet it remains valuable for baseline detections. While we provide additional + rules for SSH or cloud vendor-specific credentials, you can significantly enhance your security + program by crafting custom rules for critical application credentials unique to your environment. + condition: > + open_read + and sensitive_files and proc_name_exists and not proc.name in (user_mgmt_binaries, userexec_binaries, package_mgmt_binaries, cron_binaries, read_sensitive_file_binaries, shell_binaries, hids_binaries, @@ -1494,35 +428,9 @@ and not linux_bench_reading_etc_shadow and not user_known_read_sensitive_files_activities and not user_read_sensitive_file_containers - output: > - Sensitive file opened for reading by non-trusted program (user=%user.name user_loginuid=%user.loginuid program=%proc.name - command=%proc.cmdline pid=%proc.pid file=%fd.name parent=%proc.pname gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] container_id=%container.id image=%container.image.repository) + output: Sensitive file opened for reading by non-trusted program (file=%fd.name gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) priority: WARNING - tags: [host, container, filesystem, mitre_credential_access, mitre_discovery, T1555, T1212, T1020, T1552, T1083] - -- macro: amazon_linux_running_python_yum - condition: > - (proc.name = python and - proc.pcmdline = "python -m amazon_linux_extras system_motd" and - proc.cmdline startswith "python -c import yum;") - -- macro: user_known_write_rpm_database_activities - condition: (never_true) - -# Only let rpm-related programs write to the rpm database -- rule: Write below rpm database - desc: an attempt to write to the rpm database by any non-rpm related program - condition: > - fd.name startswith /var/lib/rpm and open_write - and not rpm_procs - and not ansible_running_python - and not python_running_chef - and not exe_running_docker_save - and not amazon_linux_running_python_yum - and not user_known_write_rpm_database_activities - output: "Rpm database opened for writing by a non-rpm program (command=%proc.cmdline pid=%proc.pid file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline container_id=%container.id image=%container.image.repository)" - priority: ERROR - tags: [host, container, filesystem, software_mgmt, mitre_persistence, T1072] + tags: [maturity_stable, host, container, filesystem, mitre_credential_access, T1555] - macro: postgres_running_wal_e condition: (proc.pname=postgres and (proc.cmdline startswith "sh -c envdir /etc/wal-e.d/env /usr/local/bin/wal-e" or proc.cmdline startswith "sh -c envdir \"/run/etc/wal-e.d/env\" wal-g wal-push")) @@ -1542,104 +450,13 @@ condition: (proc.aname[2]=rabbitmqctl and proc.cmdline startswith "sh -c ") - macro: run_by_appdynamics - condition: (proc.pname=java and proc.pcmdline startswith "java -jar -Dappdynamics") - -- macro: user_known_db_spawned_processes - condition: (never_true) - -- rule: DB program spawned process - desc: > - a database-server related program spawned a new process other than itself. - This shouldn\'t occur and is a follow on from some SQL injection attacks. - condition: > - proc.pname in (db_server_binaries) - and spawned_process - and not proc.name in (db_server_binaries) - and not postgres_running_wal_e - and not user_known_db_spawned_processes - output: > - Database-related program spawned process other than itself (user=%user.name user_loginuid=%user.loginuid - program=%proc.cmdline pid=%proc.pid parent=%proc.pname container_id=%container.id image=%container.image.repository) - priority: NOTICE - tags: [host, container, process, database, mitre_execution, T1190] - -- macro: user_known_modify_bin_dir_activities - condition: (never_true) - -- rule: Modify binary dirs - desc: an attempt to modify any file below a set of binary directories. - condition: bin_dir_rename and modify and not package_mgmt_procs and not exe_running_docker_save and not user_known_modify_bin_dir_activities - output: > - File below known binary directory renamed/removed (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid - pcmdline=%proc.pcmdline operation=%evt.type file=%fd.name %evt.args container_id=%container.id image=%container.image.repository) - priority: ERROR - tags: [host, container, filesystem, mitre_persistence, T1222.002] - -- macro: user_known_mkdir_bin_dir_activities - condition: (never_true) - -- rule: Mkdir binary dirs - desc: an attempt to create a directory below a set of binary directories. - condition: > - mkdir - and bin_dir_mkdir - and not package_mgmt_procs - and not user_known_mkdir_bin_dir_activities - and not exe_running_docker_save - output: > - Directory below known binary directory created (user=%user.name user_loginuid=%user.loginuid - command=%proc.cmdline pid=%proc.pid directory=%evt.arg.path container_id=%container.id image=%container.image.repository) - priority: ERROR - tags: [host, container, filesystem, mitre_persistence, T1222.002] - -# This list allows for easy additions to the set of commands allowed -# to change thread namespace without having to copy and override the -# entire change thread namespace rule. -- list: user_known_change_thread_namespace_binaries - items: [crio, multus] - -- macro: user_known_change_thread_namespace_activities - condition: (never_true) - -- list: network_plugin_binaries - items: [aws-cni, azure-vnet] - -- macro: weaveworks_scope - condition: (container.image.repository endswith weaveworks/scope and proc.name=scope) - -- rule: Change thread namespace - desc: > - an attempt to change a program/thread\'s namespace (commonly done - as a part of creating a container) by calling setns. - condition: > - evt.type=setns and evt.dir=< - and proc_name_exists - and not (container.id=host and proc.name in (docker_binaries, k8s_binaries, lxd_binaries, nsenter)) - and not proc.name in (sysdigcloud_binaries, sysdig, calico, oci-umount, cilium-cni, network_plugin_binaries) - and not proc.name in (user_known_change_thread_namespace_binaries) - and not proc.name startswith "runc" - and not proc.cmdline startswith "containerd" - and not proc.pname in (sysdigcloud_binaries, hyperkube, kubelet, protokube, dockerd, tini, aws) - and not java_running_sdjagent - and not kubelet_running_loopback - and not rancher_agent - and not rancher_network_manager - and not calico_node - and not weaveworks_scope - and not user_known_change_thread_namespace_activities - enabled: false - output: > - Namespace change (setns) by unexpected program (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid - parent=%proc.pname %container.info container_id=%container.id image=%container.image.repository:%container.image.tag) - priority: NOTICE - tags: [host, container, process, mitre_privilege_escalation, mitre_lateral_movement, T1611] + condition: (proc.pexe endswith java and proc.pcmdline contains " -jar -Dappdynamics") # The binaries in this list and their descendents are *not* allowed # spawn shells. This includes the binaries spawning shells directly as # well as indirectly. For example, apache -> php/perl for # mod_{php,perl} -> some shell is also not allowed, because the shell # has apache as an ancestor. - - list: protected_shell_spawning_binaries items: [ http_server_binaries, db_server_binaries, nosql_server_binaries, mail_binaries, @@ -1647,31 +464,31 @@ ] - macro: parent_java_running_zookeeper - condition: (proc.pname=java and proc.pcmdline contains org.apache.zookeeper.server) + condition: (proc.pexe endswith java and proc.pcmdline contains org.apache.zookeeper.server) - macro: parent_java_running_kafka - condition: (proc.pname=java and proc.pcmdline contains kafka.Kafka) + condition: (proc.pexe endswith java and proc.pcmdline contains kafka.Kafka) - macro: parent_java_running_elasticsearch - condition: (proc.pname=java and proc.pcmdline contains org.elasticsearch.bootstrap.Elasticsearch) + condition: (proc.pexe endswith java and proc.pcmdline contains org.elasticsearch.bootstrap.Elasticsearch) - macro: parent_java_running_activemq - condition: (proc.pname=java and proc.pcmdline contains activemq.jar) + condition: (proc.pexe endswith java and proc.pcmdline contains activemq.jar) - macro: parent_java_running_cassandra - condition: (proc.pname=java and (proc.pcmdline contains "-Dcassandra.config.loader" or proc.pcmdline contains org.apache.cassandra.service.CassandraDaemon)) + condition: (proc.pexe endswith java and (proc.pcmdline contains "-Dcassandra.config.loader" or proc.pcmdline contains org.apache.cassandra.service.CassandraDaemon)) - macro: parent_java_running_jboss_wildfly - condition: (proc.pname=java and proc.pcmdline contains org.jboss) + condition: (proc.pexe endswith java and proc.pcmdline contains org.jboss) - macro: parent_java_running_glassfish - condition: (proc.pname=java and proc.pcmdline contains com.sun.enterprise.glassfish) + condition: (proc.pexe endswith java and proc.pcmdline contains com.sun.enterprise.glassfish) - macro: parent_java_running_hadoop - condition: (proc.pname=java and proc.pcmdline contains org.apache.hadoop) + condition: (proc.pexe endswith java and proc.pcmdline contains org.apache.hadoop) - macro: parent_java_running_datastax - condition: (proc.pname=java and proc.pcmdline contains com.datastax) + condition: (proc.pexe endswith java and proc.pcmdline contains com.datastax) - macro: nginx_starting_nginx condition: (proc.pname=nginx and proc.cmdline contains "/usr/sbin/nginx -c /etc/nginx/nginx.conf") @@ -1718,7 +535,7 @@ # # Like for node, we make this case opt-in. - macro: possibly_parent_java_running_tomcat - condition: (never_true and proc.pname=java and proc.pcmdline contains org.apache.catalina.startup.Bootstrap) + condition: (never_true and proc.pexe endswith java and proc.pcmdline contains org.apache.catalina.startup.Bootstrap) - macro: protected_shell_spawner condition: > @@ -1743,7 +560,16 @@ # (the ./run and ./finish scripts), but the processes runsv can not # spawn shells. - rule: Run shell untrusted - desc: an attempt to spawn a shell below a non-shell application. Specific applications are monitored. + desc: > + An attempt to spawn a shell below a non-shell application. The non-shell applications that are monitored are + defined in the protected_shell_spawner macro, with protected_shell_spawning_binaries being the list you can + easily customize. For Java parent processes, please note that Java often has a custom process name. Therefore, + rely more on proc.exe to define Java applications. This rule can be noisier, as you can see in the exhaustive + existing tuning. However, given it is very behavior-driven and broad, it is universally relevant to catch + general Remote Code Execution (RCE). Allocate time to tune this rule for your use cases and reduce noise. + Tuning suggestions include looking at the duration of the parent process (proc.ppid.duration) to define your + long-running app processes. Checking for newer fields such as proc.vpgid.name and proc.vpgid.exe instead of the + direct parent process being a non-shell application could make the rule more robust. condition: > spawned_process and shell_procs @@ -1774,74 +600,9 @@ and not rabbitmqctl_running_scripts and not run_by_appdynamics and not user_shell_container_exclusions - output: > - Shell spawned by untrusted binary (user=%user.name user_loginuid=%user.loginuid shell=%proc.name parent=%proc.pname - cmdline=%proc.cmdline pid=%proc.pid pcmdline=%proc.pcmdline gparent=%proc.aname[2] ggparent=%proc.aname[3] - aname[4]=%proc.aname[4] aname[5]=%proc.aname[5] aname[6]=%proc.aname[6] aname[7]=%proc.aname[7] container_id=%container.id image=%container.image.repository) - priority: DEBUG - tags: [host, container, process, shell, mitre_execution, T1059.004] - -- macro: allowed_openshift_registry_root - condition: > - (container.image.repository startswith openshift3/ or - container.image.repository startswith registry.redhat.io/openshift3/ or - container.image.repository startswith registry.access.redhat.com/openshift3/) - -# Source: https://docs.openshift.com/enterprise/3.2/install_config/install/disconnected_install.html -- macro: openshift_image - condition: > - (allowed_openshift_registry_root and - (container.image.repository endswith /logging-deployment or - container.image.repository endswith /logging-elasticsearch or - container.image.repository endswith /logging-kibana or - container.image.repository endswith /logging-fluentd or - container.image.repository endswith /logging-auth-proxy or - container.image.repository endswith /metrics-deployer or - container.image.repository endswith /metrics-hawkular-metrics or - container.image.repository endswith /metrics-cassandra or - container.image.repository endswith /metrics-heapster or - container.image.repository endswith /ose-haproxy-router or - container.image.repository endswith /ose-deployer or - container.image.repository endswith /ose-sti-builder or - container.image.repository endswith /ose-docker-builder or - container.image.repository endswith /ose-pod or - container.image.repository endswith /ose-node or - container.image.repository endswith /ose-docker-registry or - container.image.repository endswith /prometheus-node-exporter or - container.image.repository endswith /image-inspector)) - -- list: redhat_io_images_privileged - items: [registry.redhat.io/openshift-logging/fluentd-rhel8, registry.redhat.io/openshift4/ose-csi-node-driver-registrar, registry.redhat.io/openshift4/ose-kubernetes-nmstate-handler-rhel8, registry.redhat.io/openshift4/ose-local-storage-diskmaker] - -- macro: redhat_image - condition: > - (container.image.repository in (redhat_io_images_privileged)) - -# https://docs.aws.amazon.com/eks/latest/userguide/add-ons-images.html -# official AWS EKS registry list. AWS has different ECR repo per region -- macro: allowed_aws_ecr_registry_root_for_eks - condition: > - (container.image.repository startswith "602401143452.dkr.ecr" or - container.image.repository startswith "877085696533.dkr.ecr" or - container.image.repository startswith "800184023465.dkr.ecr" or - container.image.repository startswith "918309763551.dkr.ecr" or - container.image.repository startswith "961992271922.dkr.ecr" or - container.image.repository startswith "590381155156.dkr.ecr" or - container.image.repository startswith "558608220178.dkr.ecr" or - container.image.repository startswith "151742754352.dkr.ecr" or - container.image.repository startswith "013241004608.dkr.ecr") - - -- macro: aws_eks_core_images - condition: > - (allowed_aws_ecr_registry_root_for_eks and - (container.image.repository endswith ".amazonaws.com/amazon-k8s-cni" or - container.image.repository endswith ".amazonaws.com/eks/kube-proxy")) - - -- macro: aws_eks_image_sensitive_mount - condition: > - (allowed_aws_ecr_registry_root_for_eks and container.image.repository endswith ".amazonaws.com/amazon-k8s-cni") + output: Shell spawned by untrusted binary (parent_exe=%proc.pexe parent_exepath=%proc.pexepath pcmdline=%proc.pcmdline gparent=%proc.aname[2] ggparent=%proc.aname[3] aname[4]=%proc.aname[4] aname[5]=%proc.aname[5] aname[6]=%proc.aname[6] aname[7]=%proc.aname[7] evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) + priority: NOTICE + tags: [maturity_stable, host, container, process, shell, mitre_execution, T1059.004] # These images are allowed both to run with --privileged and to mount # sensitive paths from the host filesystem. @@ -1853,20 +614,6 @@ - list: trusted_images items: [] -# Add conditions to this macro (probably in a separate file, -# overwriting this macro) to specify additional containers that are -# trusted and therefore allowed to run privileged *and* with sensitive -# mounts. -# -# Like trusted_images, this is deprecated in favor of -# user_privileged_containers and user_sensitive_mount_containers and -# is only provided for backwards compatibility. -# -# In this file, it just takes one of the images in trusted_containers -# and repeats it. -- macro: user_trusted_containers - condition: (never_true) - - list: sematext_images items: [docker.io/sematext/sematext-agent-docker, docker.io/sematext/agent, docker.io/sematext/logagent, registry.access.redhat.com/sematext/sematext-agent-docker, @@ -1888,7 +635,6 @@ - public.ecr.aws/falcosecurity/falco-no-driver # These container images are allowed to run with --privileged and full set of capabilities -# TODO: Remove k8s.gcr.io reference after 01/Dec/2023 - list: falco_privileged_images items: [ falco_containers, @@ -1907,123 +653,16 @@ gke.gcr.io/netd-amd64, gke.gcr.io/watcher-daemonset, gcr.io/google-containers/prometheus-to-sd, - k8s.gcr.io/ip-masq-agent-amd64, - k8s.gcr.io/kube-proxy, - k8s.gcr.io/prometheus-to-sd, registry.k8s.io/ip-masq-agent-amd64, registry.k8s.io/kube-proxy, registry.k8s.io/prometheus-to-sd, quay.io/calico/node, sysdig/sysdig, sematext_images, - k8s.gcr.io/dns/k8s-dns-node-cache, - registry.k8s.io/dns/k8s-dns-node-cache + registry.k8s.io/dns/k8s-dns-node-cache, + mcr.microsoft.com/oss/kubernetes/kube-proxy ] -- macro: falco_privileged_containers - condition: (openshift_image or - user_trusted_containers or - aws_eks_core_images or - container.image.repository in (trusted_images) or - container.image.repository in (falco_privileged_images) or - container.image.repository startswith istio/proxy_ or - container.image.repository startswith quay.io/sysdig/) - -# Add conditions to this macro (probably in a separate file, -# overwriting this macro) to specify additional containers that are -# allowed to run privileged -# -# In this file, it just takes one of the images in falco_privileged_images -# and repeats it. -- macro: user_privileged_containers - condition: (never_true) - -# These container images are allowed to mount sensitive paths from the -# host filesystem. -- list: falco_sensitive_mount_images - items: [ - falco_containers, - docker.io/sysdig/sysdig, sysdig/sysdig, - gcr.io/google_containers/hyperkube, - gcr.io/google_containers/kube-proxy, docker.io/calico/node, - docker.io/rook/toolbox, docker.io/cloudnativelabs/kube-router, docker.io/consul, - docker.io/datadog/docker-dd-agent, docker.io/datadog/agent, docker.io/docker/ucp-agent, docker.io/gliderlabs/logspout, - docker.io/netdata/netdata, docker.io/google/cadvisor, docker.io/prom/node-exporter, - amazon/amazon-ecs-agent, prom/node-exporter, amazon/cloudwatch-agent - ] - -- macro: falco_sensitive_mount_containers - condition: (user_trusted_containers or - aws_eks_image_sensitive_mount or - container.image.repository in (trusted_images) or - container.image.repository in (falco_sensitive_mount_images) or - container.image.repository startswith quay.io/sysdig/ or - container.image.repository=k8scloudprovider/cinder-csi-plugin) - -# Add conditions to this macro (probably in a separate file, -# overwriting this macro) to specify additional containers that are -# allowed to perform sensitive mounts. -# -# In this file, it just takes one of the images in falco_sensitive_mount_images -# and repeats it. -- macro: user_sensitive_mount_containers - condition: (never_true) - -- rule: Launch Privileged Container - desc: Detect the initial process started in a privileged container. Exceptions are made for known trusted images. - condition: > - container_started and container - and container.privileged=true - and not falco_privileged_containers - and not user_privileged_containers - and not redhat_image - output: Privileged container started (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid %container.info image=%container.image.repository:%container.image.tag) - priority: INFO - tags: [container, cis, mitre_privilege_escalation, mitre_lateral_movement, T1610] - -# These capabilities were used in the past to escape from containers -- macro: excessively_capable_container - condition: > - (thread.cap_permitted contains CAP_SYS_ADMIN - or thread.cap_permitted contains CAP_SYS_MODULE - or thread.cap_permitted contains CAP_SYS_RAWIO - or thread.cap_permitted contains CAP_SYS_PTRACE - or thread.cap_permitted contains CAP_SYS_BOOT - or thread.cap_permitted contains CAP_SYSLOG - or thread.cap_permitted contains CAP_DAC_READ_SEARCH - or thread.cap_permitted contains CAP_NET_ADMIN - or thread.cap_permitted contains CAP_BPF) - -- rule: Launch Excessively Capable Container - desc: Detect container started with a powerful set of capabilities. Exceptions are made for known trusted images. - condition: > - container_started and container - and excessively_capable_container - and not falco_privileged_containers - and not user_privileged_containers - output: Excessively capable container started (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid %container.info image=%container.image.repository:%container.image.tag cap_permitted=%thread.cap_permitted) - priority: INFO - tags: [container, cis, mitre_privilege_escalation, mitre_lateral_movement, T1610] - - -# For now, only considering a full mount of /etc as -# sensitive. Ideally, this would also consider all subdirectories -# below /etc as well, but the globbing mechanism -# doesn't allow exclusions of a full pattern, only single characters. -- macro: sensitive_mount - condition: (container.mount.dest[/proc*] != "N/A" or - container.mount.dest[/var/run/docker.sock] != "N/A" or - container.mount.dest[/var/run/crio/crio.sock] != "N/A" or - container.mount.dest[/run/containerd/containerd.sock] != "N/A" or - container.mount.dest[/var/lib/kubelet] != "N/A" or - container.mount.dest[/var/lib/kubelet/pki] != "N/A" or - container.mount.dest[/] != "N/A" or - container.mount.dest[/home/admin] != "N/A" or - container.mount.dest[/etc] != "N/A" or - container.mount.dest[/etc/kubernetes] != "N/A" or - container.mount.dest[/etc/kubernetes/manifests] != "N/A" or - container.mount.dest[/root*] != "N/A") - # The steps libcontainer performs to set up the root program for a container are: # - clone + exec self to a program runc:[0:PARENT] # - clone a program runc:[1:CHILD] which sets up all the namespaces @@ -2034,42 +673,8 @@ # already exited, or might still be around. So we handle both. # We also let runc:[1:CHILD] count as the parent process, which can occur # when we lose events and lose track of state. - - macro: container_entrypoint - condition: (not proc.pname exists or proc.pname in (runc:[0:PARENT], runc:[1:CHILD], runc, docker-runc, exe, docker-runc-cur)) - -- rule: Launch Sensitive Mount Container - desc: > - Detect the initial process started by a container that has a mount from a sensitive host directory - (i.e. /proc). Exceptions are made for known trusted images. - condition: > - container_started and container - and sensitive_mount - and not falco_sensitive_mount_containers - and not user_sensitive_mount_containers - output: Container with sensitive mount started (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid %container.info image=%container.image.repository:%container.image.tag mounts=%container.mounts) - priority: INFO - tags: [container, cis, mitre_lateral_movement, T1610] - -# In a local/user rules file, you could override this macro to -# explicitly enumerate the container images that you want to run in -# your environment. In this main falco rules file, there isn't any way -# to know all the containers that can run, so any container is -# allowed, by using a filter that is guaranteed to evaluate to true. -# In the overridden macro, the condition would look something like -# (container.image.repository = vendor/container-1 or -# container.image.repository = vendor/container-2 or ...) - -- macro: allowed_containers - condition: (container.id exists) - -- rule: Launch Disallowed Container - desc: > - Detect the initial process started by a container that is not in a list of allowed containers. - condition: container_started and container and not allowed_containers - output: Container started and not in allowed list (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid %container.info image=%container.image.repository:%container.image.tag) - priority: WARNING - tags: [container, mitre_lateral_movement, T1610] + condition: (not proc.pname exists or proc.pname in (runc:[0:PARENT], runc:[1:CHILD], runc, docker-runc, exe, docker-runc-cur, containerd-shim, systemd, crio)) - macro: user_known_system_user_login condition: (never_true) @@ -2078,13 +683,24 @@ # - condition: evt.type != switch and user.name = root and proc.name != sshd and interactive # output: "Interactive root (%user.name %proc.name %evt.dir %evt.type %evt.args %fd.name)" # priority: WARNING - - rule: System user interactive - desc: an attempt to run interactive commands by a system (i.e. non-login) user - condition: spawned_process and system_users and interactive and not user_known_system_user_login - output: "System user ran an interactive command (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid container_id=%container.id image=%container.image.repository)" + desc: > + System (e.g. non-login) users spawning new processes. Can add custom service users (e.g. apache or mysqld). + 'Interactive' is defined as new processes as descendants of an ssh session or login process. Consider further tuning + by only looking at processes in a terminal / tty (proc.tty != 0). A newer field proc.is_vpgid_leader could be of help + to distinguish if the process was "directly" executed, for instance, in a tty, or executed as a descendant process in the + same process group, which, for example, is the case when subprocesses are spawned from a script. Consider this rule + as a great template rule to monitor interactive accesses to your systems more broadly. However, such a custom rule would be + unique to your environment. The rule "Terminal shell in container" that fires when using "kubectl exec" is more Kubernetes + relevant, whereas this one could be more interesting for the underlying host. + condition: > + spawned_process + and system_users + and interactive + and not user_known_system_user_login + output: System user ran an interactive command (evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) priority: INFO - tags: [host, container, users, mitre_execution, T1059] + tags: [maturity_stable, host, container, users, mitre_execution, T1059, NIST_800-53_AC-2] # In some cases, a shell is expected to be run in a container. For example, configuration # management software may do this, which is expected. @@ -2092,23 +708,27 @@ condition: (never_true) - rule: Terminal shell in container - desc: A shell was used as the entrypoint/exec point into a container with an attached terminal. + desc: > + A shell was used as the entrypoint/exec point into a container with an attached terminal. Parent process may have + legitimately already exited and be null (read container_entrypoint macro). Common when using "kubectl exec" in Kubernetes. + Correlate with k8saudit exec logs if possible to find user or serviceaccount token used (fuzzy correlation by namespace and pod name). + Rather than considering it a standalone rule, it may be best used as generic auditing rule while examining other triggered + rules in this container/tty. condition: > - spawned_process and container - and shell_procs and proc.tty != 0 + spawned_process + and container + and shell_procs + and proc.tty != 0 and container_entrypoint and not user_expected_terminal_shell_in_container_conditions - output: > - A shell was spawned in a container with an attached terminal (user=%user.name user_loginuid=%user.loginuid %container.info - shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline pid=%proc.pid terminal=%proc.tty container_id=%container.id image=%container.image.repository) + output: A shell was spawned in a container with an attached terminal (evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) priority: NOTICE - tags: [container, shell, mitre_execution, T1059] + tags: [maturity_stable, container, shell, mitre_execution, T1059] # For some container types (mesos), there isn't a container image to # work with, and the container name is autogenerated, so there isn't # any stable aspect of the software to work with. In this case, we # fall back to allowing certain command lines. - - list: known_shell_spawn_cmdlines items: [ '"sh -c uname -p 2> /dev/null"', @@ -2161,286 +781,6 @@ - macro: user_shell_container_exclusions condition: (never_true) -- macro: login_doing_dns_lookup - condition: (proc.name=login and fd.l4proto=udp and fd.sport=53) - -# sockfamily ip is to exclude certain processes (like 'groups') that communicate on unix-domain sockets -# systemd can listen on ports to launch things like sshd on demand -- rule: System procs network activity - desc: any network activity performed by system binaries that are not expected to send or receive any network traffic - condition: > - (fd.sockfamily = ip and (system_procs or proc.name in (shell_binaries))) - and (inbound_outbound) - and not proc.name in (known_system_procs_network_activity_binaries) - and not login_doing_dns_lookup - and not user_expected_system_procs_network_activity_conditions - output: > - Known system binary sent/received network traffic - (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid connection=%fd.name container_id=%container.id image=%container.image.repository) - priority: NOTICE - tags: [host, container, network, mitre_exfiltration, T1059, TA0011] - -# This list allows easily whitelisting system proc names that are -# expected to communicate on the network. -- list: known_system_procs_network_activity_binaries - items: [systemd, hostid, id] - -# This macro allows specifying conditions under which a system binary -# is allowed to communicate on the network. For instance, only specific -# proc.cmdline values could be allowed to be more granular in what is -# allowed. -- macro: user_expected_system_procs_network_activity_conditions - condition: (never_true) - -# When filled in, this should look something like: -# (proc.env contains "HTTP_PROXY=http://my.http.proxy.com ") -# The trailing space is intentional so avoid matching on prefixes of -# the actual proxy. -- macro: allowed_ssh_proxy_env - condition: (always_true) - -- list: http_proxy_binaries - items: [curl, wget] - -- macro: http_proxy_procs - condition: (proc.name in (http_proxy_binaries)) - -- rule: Program run with disallowed http proxy env - desc: An attempt to run a program with a disallowed HTTP_PROXY environment variable - condition: > - spawned_process and - http_proxy_procs and - not allowed_ssh_proxy_env and - proc.env icontains HTTP_PROXY - enabled: false - output: > - Program run with disallowed HTTP_PROXY environment variable - (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid env=%proc.env parent=%proc.pname container_id=%container.id image=%container.image.repository) - priority: NOTICE - tags: [host, container, users, mitre_command_and_control, T1090, T1204] - -# In some environments, any attempt by a interpreted program (perl, -# python, ruby, etc) to listen for incoming connections or perform -# outgoing connections might be suspicious. These rules are not -# enabled by default. - -- rule: Interpreted procs inbound network activity - desc: Any inbound network activity performed by any interpreted program (perl, python, ruby, etc.) - condition: > - (inbound and interpreted_procs) - enabled: false - output: > - Interpreted program received/listened for network traffic - (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid connection=%fd.name container_id=%container.id image=%container.image.repository) - priority: NOTICE - tags: [host, container, network, mitre_exfiltration, TA0011] - -- rule: Interpreted procs outbound network activity - desc: Any outbound network activity performed by any interpreted program (perl, python, ruby, etc.) - condition: > - (outbound and interpreted_procs) - enabled: false - output: > - Interpreted program performed outgoing network connection - (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid connection=%fd.name container_id=%container.id image=%container.image.repository) - priority: NOTICE - tags: [host, container, network, mitre_exfiltration, TA0011] - -- list: openvpn_udp_ports - items: [1194, 1197, 1198, 8080, 9201] - -- list: l2tp_udp_ports - items: [500, 1701, 4500, 10000] - -- list: statsd_ports - items: [8125] - -- list: ntp_ports - items: [123] - -# Some applications will connect a udp socket to an address only to -# test connectivity. Assuming the udp connect works, they will follow -# up with a tcp connect that actually sends/receives data. -# -# With that in mind, we listed a few commonly seen ports here to avoid -# some false positives. In addition, we make the main rule opt-in, so -# it's disabled by default. - -- list: test_connect_ports - items: [0, 9, 80, 3306] - -- list: expected_udp_ports - items: [53, openvpn_udp_ports, l2tp_udp_ports, statsd_ports, ntp_ports, test_connect_ports] - -- macro: expected_udp_traffic - condition: fd.port in (expected_udp_ports) - -- rule: Unexpected UDP Traffic - desc: UDP traffic not on port 53 (DNS) or other commonly used ports - condition: (inbound_outbound) and fd.l4proto=udp and not expected_udp_traffic - enabled: false - output: > - Unexpected UDP Traffic Seen - (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid connection=%fd.name proto=%fd.l4proto evt=%evt.type %evt.args container_id=%container.id image=%container.image.repository) - priority: NOTICE - tags: [host, container, network, mitre_exfiltration, TA0011] - -# With the current restriction on system calls handled by falco -# (e.g. excluding read/write/sendto/recvfrom/etc, this rule won't -# trigger). -# - rule: Ssh error in syslog -# desc: any ssh errors (failed logins, disconnects, ...) sent to syslog -# condition: syslog and ssh_error_message and evt.dir = < -# output: "sshd sent error message to syslog (error=%evt.buffer)" -# priority: WARNING - -- macro: somebody_becoming_themselves - condition: ((user.name=nobody and evt.arg.uid=nobody) or - (user.name=www-data and evt.arg.uid=www-data) or - (user.name=_apt and evt.arg.uid=_apt) or - (user.name=postfix and evt.arg.uid=postfix) or - (user.name=pki-agent and evt.arg.uid=pki-agent) or - (user.name=pki-acme and evt.arg.uid=pki-acme) or - (user.name=nfsnobody and evt.arg.uid=nfsnobody) or - (user.name=postgres and evt.arg.uid=postgres)) - -- macro: nrpe_becoming_nagios - condition: (proc.name=nrpe and evt.arg.uid=nagios) - -# In containers, the user name might be for a uid that exists in the -# container but not on the host. (See -# https://github.com/draios/sysdig/issues/954). So in that case, allow -# a setuid. -- macro: known_user_in_container - condition: (container and user.name != "N/A") - -# Add conditions to this macro (probably in a separate file, -# overwriting this macro) to allow for specific combinations of -# programs changing users by calling setuid. -# -# In this file, it just takes one of the condition in the base macro -# and repeats it. -- macro: user_known_non_sudo_setuid_conditions - condition: user.name=root - -# sshd, mail programs attempt to setuid to root even when running as non-root. Excluding here to avoid meaningless FPs -- rule: Non sudo setuid - desc: > - an attempt to change users by calling setuid. sudo/su are excluded. users "root" and "nobody" - suing to itself are also excluded, as setuid calls typically involve dropping privileges. - condition: > - evt.type=setuid and evt.dir=> - and (known_user_in_container or not container) - and not (user.name=root or user.uid=0) - and not somebody_becoming_themselves - and not proc.name in (known_setuid_binaries, userexec_binaries, mail_binaries, docker_binaries, - nomachine_binaries) - and not proc.name startswith "runc:" - and not java_running_sdjagent - and not nrpe_becoming_nagios - and not user_known_non_sudo_setuid_conditions - output: > - Unexpected setuid call by non-sudo, non-root program (user=%user.name user_loginuid=%user.loginuid cur_uid=%user.uid parent=%proc.pname - command=%proc.cmdline pid=%proc.pid uid=%evt.arg.uid container_id=%container.id image=%container.image.repository) - priority: NOTICE - tags: [host, container, users, mitre_privilege_escalation, T1548.001] - -- macro: user_known_user_management_activities - condition: (never_true) - -- macro: chage_list - condition: (proc.name=chage and (proc.cmdline contains "-l" or proc.cmdline contains "--list")) - -- rule: User mgmt binaries - desc: > - activity by any programs that can manage users, passwords, or permissions. sudo and su are excluded. - Activity in containers is also excluded--some containers create custom users on top - of a base linux distribution at startup. - Some innocuous command lines that don't actually change anything are excluded. - condition: > - spawned_process and proc.name in (user_mgmt_binaries) and - not proc.name in (su, sudo, lastlog, nologin, unix_chkpwd) and not container and - not proc.pname in (cron_binaries, systemd, systemd.postins, udev.postinst, run-parts) and - not proc.cmdline startswith "passwd -S" and - not proc.cmdline startswith "useradd -D" and - not proc.cmdline startswith "systemd --version" and - not run_by_qualys and - not run_by_sumologic_securefiles and - not run_by_yum and - not run_by_ms_oms and - not run_by_google_accounts_daemon and - not chage_list and - not user_known_user_management_activities - output: > - User management binary command run outside of container - (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid parent=%proc.pname gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4]) - priority: NOTICE - tags: [host, container, users, software_mgmt, mitre_persistence, T1543, T1098] - -- list: allowed_dev_files - items: [ - /dev/null, /dev/stdin, /dev/stdout, /dev/stderr, - /dev/random, /dev/urandom, /dev/console, /dev/kmsg - ] - -- macro: user_known_create_files_below_dev_activities - condition: (never_true) - -# (we may need to add additional checks against false positives, see: -# https://bugs.launchpad.net/ubuntu/+source/rkhunter/+bug/86153) -- rule: Create files below dev - desc: creating any files below /dev other than known programs that manage devices. Some rootkits hide files in /dev. - condition: > - fd.directory = /dev and - (evt.type = creat or (evt.type in (open,openat,openat2) and evt.arg.flags contains O_CREAT)) - and not proc.name in (dev_creation_binaries) - and not fd.name in (allowed_dev_files) - and not fd.name startswith /dev/tty - and not user_known_create_files_below_dev_activities - output: "File created below /dev by untrusted program (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid file=%fd.name container_id=%container.id image=%container.image.repository)" - priority: ERROR - tags: [host, container, filesystem, mitre_persistence, T1543, T1083] - - -# In a local/user rules file, you could override this macro to -# explicitly enumerate the container images that you want to allow -# access to EC2 metadata. In this main falco rules file, there isn't -# any way to know all the containers that should have access, so any -# container is allowed, by repeating the "container" macro. In the -# overridden macro, the condition would look something like -# (container.image.repository = vendor/container-1 or -# container.image.repository = vendor/container-2 or ...) -- macro: ec2_metadata_containers - condition: container - -# On EC2 instances, 169.254.169.254 is a special IP used to fetch -# metadata about the instance. It may be desirable to prevent access -# to this IP from containers. -- rule: Contact EC2 Instance Metadata Service From Container - desc: Detect attempts to contact the EC2 Instance Metadata Service from a container - condition: outbound and fd.sip="169.254.169.254" and container and not ec2_metadata_containers - output: Outbound connection to EC2 instance metadata service (command=%proc.cmdline pid=%proc.pid connection=%fd.name %container.info image=%container.image.repository:%container.image.tag) - priority: NOTICE - enabled: false - tags: [network, aws, container, mitre_discovery, T1565] - - -# This rule is not enabled by default, since this rule is for cloud environment(GCP, AWS and Azure) only. -# You can filter the container that you want to allow access to metadata by overwriting user_known_metadata_access macro. - -- macro: user_known_metadata_access - condition: (k8s.ns.name = "kube-system") - -# On GCP, AWS and Azure, 169.254.169.254 is a special IP used to fetch -# metadata about the instance. The metadata could be used to get credentials by attackers. -- rule: Contact cloud metadata service from container - desc: Detect attempts to contact the Cloud Instance Metadata Service from a container - condition: outbound and fd.sip="169.254.169.254" and container and not user_known_metadata_access - enabled: false - output: Outbound connection to cloud instance metadata service (command=%proc.cmdline pid=%proc.pid connection=%fd.name %container.info image=%container.image.repository:%container.image.tag) - priority: NOTICE - tags: [network, container, mitre_discovery, T1565] - # Containers from IBM Cloud - list: ibm_cloud_containers items: @@ -2452,7 +792,6 @@ # allowed to contact the K8s API Server from within a container. This # might cover cases where the K8s infrastructure itself is running # within a container. -# TODO: Remove k8s.gcr.io reference after 01/Dec/2023 - macro: k8s_containers condition: > (container.image.repository in (gcr.io/google_containers/hyperkube-amd64, @@ -2464,10 +803,11 @@ ibm_cloud_containers, velero/velero, quay.io/jetstack/cert-manager-cainjector, weaveworks/kured, - quay.io/prometheus-operator/prometheus-operator, k8s.gcr.io/ingress-nginx/kube-webhook-certgen, + quay.io/prometheus-operator/prometheus-operator, registry.k8s.io/ingress-nginx/kube-webhook-certgen, quay.io/spotahome/redis-operator, registry.opensource.zalan.do/acid/postgres-operator, registry.opensource.zalan.do/acid/postgres-operator-ui, - rabbitmqoperator/cluster-operator) + rabbitmqoperator/cluster-operator, quay.io/kubecost1/kubecost-cost-model, + docker.io/bitnami/prometheus, docker.io/bitnami/kube-state-metrics, mcr.microsoft.com/oss/azure/aad-pod-identity/nmi) or (k8s.ns.name = "kube-system")) - macro: k8s_api_server @@ -2477,32 +817,23 @@ condition: (never_true) - rule: Contact K8S API Server From Container - desc: Detect attempts to contact the K8S API Server from a container - condition: > - evt.type=connect and evt.dir=< and - (fd.typechar=4 or fd.typechar=6) and - container and - not k8s_containers and - k8s_api_server and - not user_known_contact_k8s_api_server_activities - output: Unexpected connection to K8s API Server from container (command=%proc.cmdline pid=%proc.pid %container.info image=%container.image.repository:%container.image.tag connection=%fd.name) - priority: NOTICE - tags: [network, k8s, container, mitre_discovery, T1565] - -# In a local/user rules file, list the container images that are -# allowed to contact NodePort services from within a container. This -# might cover cases where the K8s infrastructure itself is running -# within a container. -# -# By default, all containers are allowed to contact NodePort services. -- macro: nodeport_containers - condition: container - -- rule: Unexpected K8s NodePort Connection - desc: Detect attempts to use K8s NodePorts from a container - condition: (inbound_outbound) and fd.sport >= 30000 and fd.sport <= 32767 and container and not nodeport_containers - output: Unexpected K8s NodePort Connection (command=%proc.cmdline pid=%proc.pid connection=%fd.name container_id=%container.id image=%container.image.repository) + desc: > + Detect attempts to communicate with the K8S API Server from a container by non-profiled users. Kubernetes APIs play a + pivotal role in configuring the cluster management lifecycle. Detecting potential unauthorized access to the API server + is of utmost importance. Audit your complete infrastructure and pinpoint any potential machines from which the API server + might be accessible based on your network layout. If Falco can't operate on all these machines, consider analyzing the + Kubernetes audit logs (typically drained from control nodes, and Falco offers a k8saudit plugin) as an additional data + source for detections within the control plane. + condition: > + evt.type=connect and evt.dir=< + and (fd.typechar=4 or fd.typechar=6) + and container + and k8s_api_server + and not k8s_containers + and not user_known_contact_k8s_api_server_activities + output: Unexpected connection to K8s API Server from container (connection=%fd.name lport=%fd.lport rport=%fd.rport fd_type=%fd.type fd_proto=fd.l4proto evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) priority: NOTICE +<<<<<<< HEAD tags: [network, k8s, container, mitre_persistence, T1205.001] - list: network_tool_binaries @@ -2542,53 +873,27 @@ Package management process launched in container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag) priority: ERROR - tags: [container, process, software_mgmt, mitre_persistence, T1505] + tags: [maturity_stable, container, network, k8s, mitre_discovery, T1565] - rule: Netcat Remote Code Execution in Container - desc: Netcat Program runs inside container that allows remote code execution - condition: > - spawned_process and container and - ((proc.name = "nc" and (proc.args contains "-e" or proc.args contains "-c")) or - (proc.name = "ncat" and (proc.args contains "--sh-exec" or proc.args contains "--exec" or proc.args contains "-e " - or proc.args contains "-c " or proc.args contains "--lua-exec")) - ) - output: > - Netcat runs inside container that allows remote code execution (user=%user.name user_loginuid=%user.loginuid - command=%proc.cmdline pid=%proc.pid container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag) + desc: > + Netcat Program runs inside container that allows remote code execution and may be utilized + as a part of a variety of reverse shell payload https://github.com/swisskyrepo/PayloadsAllTheThings/. + These programs are of higher relevance as they are commonly installed on UNIX-like operating systems. + Can fire in combination with the "Redirect STDOUT/STDIN to Network Connection in Container" + rule as it utilizes a different evt.type. + condition: > + spawned_process + and container + and ((proc.name = "nc" and (proc.cmdline contains " -e" or + proc.cmdline contains " -c")) or + (proc.name = "ncat" and (proc.args contains "--sh-exec" or + proc.args contains "--exec" or proc.args contains "-e " or + proc.args contains "-c " or proc.args contains "--lua-exec")) + ) + output: Netcat runs inside container that allows remote code execution (evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) priority: WARNING - tags: [container, network, process, mitre_execution, T1059] - -- macro: user_known_network_tool_activities - condition: (never_true) - -- rule: Launch Suspicious Network Tool in Container - desc: Detect network tools launched inside container - condition: > - spawned_process and container and network_tool_procs and not user_known_network_tool_activities - output: > - Network tool launched in container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid parent_process=%proc.pname - container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag) - priority: NOTICE - tags: [container, network, process, mitre_discovery, mitre_exfiltration, T1595, T1046] - -# This rule is not enabled by default, as there are legitimate use -# cases for these tools on hosts. If you want to enable it, modify the -# following macro. -- macro: consider_network_tools_on_host - condition: (never_true) - -- rule: Launch Suspicious Network Tool on Host - desc: Detect network tools launched on the host - condition: > - spawned_process and - not container and - consider_network_tools_on_host and - network_tool_procs and - not user_known_network_tool_activities - output: > - Network tool launched on host (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid parent_process=%proc.pname) - priority: NOTICE - tags: [host, network, process, mitre_discovery, mitre_exfiltration, T1595, T1046] + tags: [maturity_stable, container, network, process, mitre_execution, T1059] - list: grep_binaries items: [grep, egrep, fgrep] @@ -2615,19 +920,23 @@ - rule: Search Private Keys or Passwords desc: > - Detect grep private keys or passwords activity. - condition: > - (spawned_process and - ((grep_commands and private_key_or_password) or - (proc.name = "find" and (proc.args contains "id_rsa" or proc.args contains "id_dsa"))) - ) - output: > - Grep private keys or passwords activities found - (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid container_id=%container.id container_name=%container.name - image=%container.image.repository:%container.image.tag) + Detect attempts to search for private keys or passwords using the grep or find command. This is often seen with + unsophisticated attackers, as there are many ways to access files using bash built-ins that could go unnoticed. + Regardless, this serves as a solid baseline detection that can be tailored to cover these gaps while maintaining + an acceptable noise level. + condition: > + spawned_process + and ((grep_commands and private_key_or_password) or + (proc.name = "find" and (proc.args contains "id_rsa" or + proc.args contains "id_dsa" or + proc.args contains "id_ed25519" or + proc.args contains "id_ecdsa" + ) + )) + output: Grep private keys or passwords activities found (evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) priority: WARNING - tags: [host, container, process, filesystem, mitre_credential_access, T1552.001] + tags: [maturity_stable, host, container, process, filesystem, mitre_credential_access, T1552.001] - list: log_directories items: [/var/log, /dev/log] @@ -2648,19 +957,26 @@ container.image.repository endswith "openshift3/ose-logging-fluentd" or container.image.repository endswith "containernetworking/azure-npm") +- macro: containerd_activities + condition: (proc.name=containerd and (fd.name startswith "/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/" or + fd.name startswith "/var/lib/containerd/tmpmounts/")) + - rule: Clear Log Activities - desc: Detect clearing of critical log files - condition: > - open_write and - access_log_files and - evt.arg.flags contains "O_TRUNC" and - not trusted_logging_images and - not allowed_clear_log_files - output: > - Log files were tampered (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid file=%fd.name container_id=%container.id image=%container.image.repository) + desc: > + Detect clearing of critical access log files, typically done to erase evidence that could be attributed to an adversary's + actions. To effectively customize and operationalize this detection, check for potentially missing log file destinations + relevant to your environment, and adjust the profiled containers you wish not to be alerted on. + condition: > + open_write + and access_log_files + and evt.arg.flags contains "O_TRUNC" + and not containerd_activities + and not trusted_logging_images + and not allowed_clear_log_files + output: Log files were tampered (file=%fd.name evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) priority: WARNING - tags: [host, container, filesystem, mitre_defense_evasion, T1070] + tags: [maturity_stable, host, container, filesystem, mitre_defense_evasion, T1070, NIST_800-53_AU-10] - list: data_remove_commands items: [shred, mkfs, mke2fs] @@ -2672,674 +988,140 @@ condition: (never_true) - rule: Remove Bulk Data from Disk - desc: Detect process running to clear bulk data from disk - condition: spawned_process and clear_data_procs and not user_known_remove_data_activities - output: > - Bulk data has been removed from disk (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid file=%fd.name container_id=%container.id image=%container.image.repository) - priority: - WARNING - tags: [host, container, process, filesystem, mitre_persistence, T1485] - -# here `ash_history` will match both `bash_history` and `ash_history` -- macro: modify_shell_history - condition: > - (modify and ( - evt.arg.name endswith "ash_history" or - evt.arg.name endswith "zsh_history" or - evt.arg.name contains "fish_read_history" or - evt.arg.name endswith "fish_history" or - evt.arg.oldpath endswith "ash_history" or - evt.arg.oldpath endswith "zsh_history" or - evt.arg.oldpath contains "fish_read_history" or - evt.arg.oldpath endswith "fish_history" or - evt.arg.path endswith "ash_history" or - evt.arg.path endswith "zsh_history" or - evt.arg.path contains "fish_read_history" or - evt.arg.path endswith "fish_history")) - -# here `ash_history` will match both `bash_history` and `ash_history` -- macro: truncate_shell_history - condition: > - (open_write and ( - fd.name endswith "ash_history" or - fd.name endswith "zsh_history" or - fd.name contains "fish_read_history" or - fd.name endswith "fish_history") and evt.arg.flags contains "O_TRUNC") - -- macro: var_lib_docker_filepath - condition: (evt.arg.name startswith /var/lib/docker or fd.name startswith /var/lib/docker) - -- rule: Delete or rename shell history - desc: Detect shell history deletion - condition: > - (modify_shell_history or truncate_shell_history) and - not var_lib_docker_filepath and - not proc.name in (docker_binaries) - output: > - Shell history had been deleted or renamed (user=%user.name user_loginuid=%user.loginuid type=%evt.type command=%proc.cmdline pid=%proc.pid fd.name=%fd.name name=%evt.arg.name path=%evt.arg.path oldpath=%evt.arg.oldpath %container.info) - priority: - WARNING - tags: [host, container, process, filesystem, mitre_defense_evasion, T1070] - -# This rule is deprecated and will/should never be triggered. Keep it here for backport compatibility. -# Rule Delete or rename shell history is the preferred rule to use now. -- rule: Delete Bash History - desc: Detect bash history deletion - condition: > - ((spawned_process and proc.name in (shred, rm, mv) and proc.args contains "bash_history") or - (open_write and fd.name contains "bash_history" and evt.arg.flags contains "O_TRUNC")) - output: > - Shell history had been deleted or renamed (user=%user.name user_loginuid=%user.loginuid type=%evt.type command=%proc.cmdline pid=%proc.pid fd.name=%fd.name name=%evt.arg.name path=%evt.arg.path oldpath=%evt.arg.oldpath %container.info) + desc: > + Detect a process running to clear bulk data from disk with the intention to destroy data, possibly interrupting availability + to systems. Profile your environment and use user_known_remove_data_activities to tune this rule. + condition: > + spawned_process + and clear_data_procs + and not user_known_remove_data_activities + output: Bulk data has been removed from disk (file=%fd.name evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) priority: WARNING - tags: [host, container, process, filesystem, mitre_defense_evasion, T1070] - -- list: user_known_chmod_applications - items: [hyperkube, kubelet, k3s-agent] - -# This macro should be overridden in user rules as needed. This is useful if a given application -# should not be ignored altogether with the user_known_chmod_applications list, but only in -# specific conditions. -- macro: user_known_set_setuid_or_setgid_bit_conditions - condition: (never_true) - -- rule: Set Setuid or Setgid bit - desc: > - When the setuid or setgid bits are set for an application, - this means that the application will run with the privileges of the owning user or group respectively. - Detect setuid or setgid bits set via chmod - condition: > - chmod and (evt.arg.mode contains "S_ISUID" or evt.arg.mode contains "S_ISGID") - and not proc.name in (user_known_chmod_applications) - and not exe_running_docker_save - and not user_known_set_setuid_or_setgid_bit_conditions - enabled: false - output: > - Setuid or setgid bit is set via chmod (fd=%evt.arg.fd filename=%evt.arg.filename mode=%evt.arg.mode user=%user.name user_loginuid=%user.loginuid process=%proc.name - command=%proc.cmdline pid=%proc.pid container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag) - priority: - NOTICE - tags: [host, container, process, users, mitre_persistence, T1548.001] - -- list: exclude_hidden_directories - items: [/root/.cassandra] - -# The rule is disabled by default. -- macro: user_known_create_hidden_file_activities - condition: (never_true) - -- rule: Create Hidden Files or Directories - desc: Detect hidden files or directories created - condition: > - ((modify and evt.arg.newpath contains "/.") or - (mkdir and evt.arg.path contains "/.") or - (open_write and evt.arg.flags contains "O_CREAT" and fd.name contains "/." and not fd.name pmatch (exclude_hidden_directories))) and - not user_known_create_hidden_file_activities - and not exe_running_docker_save - enabled: false - output: > - Hidden file or directory created (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid - file=%fd.name newpath=%evt.arg.newpath container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag) - priority: - NOTICE - tags: [host, container, filesystem, mitre_persistence, T1564.001] - -- list: remote_file_copy_binaries - items: [rsync, scp, sftp, dcp] - -- macro: remote_file_copy_procs - condition: (proc.name in (remote_file_copy_binaries)) - -# Users should overwrite this macro to specify conditions under which a -# Custom condition for use of remote file copy tool in container -- macro: user_known_remote_file_copy_activities - condition: (never_true) - -- rule: Launch Remote File Copy Tools in Container - desc: Detect remote file copy tools launched in container - condition: > - spawned_process - and container - and remote_file_copy_procs - and not user_known_remote_file_copy_activities - output: > - Remote file copy tool launched in container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid parent_process=%proc.pname - container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag) - priority: NOTICE - tags: [container, network, process, mitre_lateral_movement, mitre_exfiltration, T1020, T1210] + tags: [maturity_stable, host, container, process, filesystem, mitre_impact, T1485] - rule: Create Symlink Over Sensitive Files - desc: Detect symlink created over sensitive files - condition: > - create_symlink and - (evt.arg.target in (sensitive_file_names) or evt.arg.target in (sensitive_directory_names)) - output: > - Symlinks created over sensitive files (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid target=%evt.arg.target linkpath=%evt.arg.linkpath parent_process=%proc.pname) + desc: > + Detect symlinks created over a curated list of sensitive files or subdirectories under /etc/ or + root directories. Can be customized as needed. Refer to further and equivalent guidance within the + rule "Read sensitive file untrusted". + condition: > + create_symlink + and (evt.arg.target in (sensitive_file_names) or evt.arg.target in (sensitive_directory_names)) + output: Symlinks created over sensitive files (target=%evt.arg.target linkpath=%evt.arg.linkpath evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) priority: WARNING - tags: [host, container, filesystem, mitre_exfiltration, mitre_credential_access, T1020, T1083, T1212, T1552, T1555] + tags: [maturity_stable, host, container, filesystem, mitre_credential_access, T1555] - rule: Create Hardlink Over Sensitive Files - desc: Detect hardlink created over sensitive files - condition: > - create_hardlink and - (evt.arg.oldpath in (sensitive_file_names)) - output: > - Hardlinks created over sensitive files (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid target=%evt.arg.oldpath linkpath=%evt.arg.newpath parent_process=%proc.pname) - priority: WARNING - tags: [host, container, filesystem, mitre_exfiltration, mitre_credential_access, T1020, T1083, T1212, T1552, T1555] - -- list: miner_ports - items: [ - 25, 3333, 3334, 3335, 3336, 3357, 4444, - 5555, 5556, 5588, 5730, 6099, 6666, 7777, - 7778, 8000, 8001, 8008, 8080, 8118, 8333, - 8888, 8899, 9332, 9999, 14433, 14444, - 45560, 45700 - ] - -- list: miner_domains - items: [ - "asia1.ethpool.org","ca.minexmr.com", - "cn.stratum.slushpool.com","de.minexmr.com", - "eth-ar.dwarfpool.com","eth-asia.dwarfpool.com", - "eth-asia1.nanopool.org","eth-au.dwarfpool.com", - "eth-au1.nanopool.org","eth-br.dwarfpool.com", - "eth-cn.dwarfpool.com","eth-cn2.dwarfpool.com", - "eth-eu.dwarfpool.com","eth-eu1.nanopool.org", - "eth-eu2.nanopool.org","eth-hk.dwarfpool.com", - "eth-jp1.nanopool.org","eth-ru.dwarfpool.com", - "eth-ru2.dwarfpool.com","eth-sg.dwarfpool.com", - "eth-us-east1.nanopool.org","eth-us-west1.nanopool.org", - "eth-us.dwarfpool.com","eth-us2.dwarfpool.com", - "eu.stratum.slushpool.com","eu1.ethermine.org", - "eu1.ethpool.org","fr.minexmr.com", - "mine.moneropool.com","mine.xmrpool.net", - "pool.minexmr.com","pool.monero.hashvault.pro", - "pool.supportxmr.com","sg.minexmr.com", - "sg.stratum.slushpool.com","stratum-eth.antpool.com", - "stratum-ltc.antpool.com","stratum-zec.antpool.com", - "stratum.antpool.com","us-east.stratum.slushpool.com", - "us1.ethermine.org","us1.ethpool.org", - "us2.ethermine.org","us2.ethpool.org", - "xmr-asia1.nanopool.org","xmr-au1.nanopool.org", - "xmr-eu1.nanopool.org","xmr-eu2.nanopool.org", - "xmr-jp1.nanopool.org","xmr-us-east1.nanopool.org", - "xmr-us-west1.nanopool.org","xmr.crypto-pool.fr", - "xmr.pool.minergate.com", "rx.unmineable.com", - "ss.antpool.com","dash.antpool.com", - "eth.antpool.com","zec.antpool.com", - "xmc.antpool.com","btm.antpool.com", - "stratum-dash.antpool.com","stratum-xmc.antpool.com", - "stratum-btm.antpool.com" - ] - -- list: https_miner_domains - items: [ - "ca.minexmr.com", - "cn.stratum.slushpool.com", - "de.minexmr.com", - "fr.minexmr.com", - "mine.moneropool.com", - "mine.xmrpool.net", - "pool.minexmr.com", - "sg.minexmr.com", - "stratum-eth.antpool.com", - "stratum-ltc.antpool.com", - "stratum-zec.antpool.com", - "stratum.antpool.com", - "xmr.crypto-pool.fr", - "ss.antpool.com", - "stratum-dash.antpool.com", - "stratum-xmc.antpool.com", - "stratum-btm.antpool.com", - "btm.antpool.com" - ] - -- list: http_miner_domains - items: [ - "ca.minexmr.com", - "de.minexmr.com", - "fr.minexmr.com", - "mine.moneropool.com", - "mine.xmrpool.net", - "pool.minexmr.com", - "sg.minexmr.com", - "xmr.crypto-pool.fr" - ] - -# Add rule based on crypto mining IOCs -- macro: minerpool_https - condition: (fd.sport="443" and fd.sip.name in (https_miner_domains)) - -- macro: minerpool_http - condition: (fd.sport="80" and fd.sip.name in (http_miner_domains)) - -- macro: minerpool_other - condition: (fd.sport in (miner_ports) and fd.sip.name in (miner_domains)) - -- macro: net_miner_pool - condition: (evt.type in (sendto, sendmsg, connect) and evt.dir=< and (fd.net != "127.0.0.0/8" and not fd.snet in (rfc_1918_addresses)) and ((minerpool_http) or (minerpool_https) or (minerpool_other))) - -- macro: trusted_images_query_miner_domain_dns - condition: (container.image.repository in (falco_containers)) - -# The rule is disabled by default. -# Note: falco will send DNS request to resolve miner pool domain which may trigger alerts in your environment. -- rule: Detect outbound connections to common miner pool ports - desc: Miners typically connect to miner pools on common ports. - condition: net_miner_pool and not trusted_images_query_miner_domain_dns - enabled: false - output: Outbound connection to IP/Port flagged by https://cryptoioc.ch (command=%proc.cmdline pid=%proc.pid port=%fd.rport ip=%fd.rip container=%container.info image=%container.image.repository) - priority: CRITICAL - tags: [host, container, network, mitre_execution, T1496] - -- rule: Detect crypto miners using the Stratum protocol - desc: Miners typically specify the mining pool to connect to with a URI that begins with 'stratum+tcp' - condition: spawned_process and (proc.cmdline contains "stratum+tcp" or proc.cmdline contains "stratum2+tcp" or proc.cmdline contains "stratum+ssl" or proc.cmdline contains "stratum2+ssl") - output: Possible miner running (command=%proc.cmdline pid=%proc.pid container=%container.info image=%container.image.repository) - priority: CRITICAL - tags: [host, container, process, mitre_execution, T1496] - -- list: k8s_client_binaries - items: [docker, kubectl, crictl] - -# TODO: Remove k8s.gcr.io reference after 01/Dec/2023 -- list: user_known_k8s_ns_kube_system_images - items: [ - k8s.gcr.io/fluentd-gcp-scaler, - k8s.gcr.io/node-problem-detector/node-problem-detector, - registry.k8s.io/fluentd-gcp-scaler, - registry.k8s.io/node-problem-detector/node-problem-detector - ] - -- list: user_known_k8s_images - items: [ - mcr.microsoft.com/aks/hcp/hcp-tunnel-front - ] - -# Whitelist for known docker client binaries run inside container -# - k8s.gcr.io/fluentd-gcp-scaler / registry.k8s.io/fluentd-gcp-scaler in GCP/GKE -# TODO: Remove k8s.gcr.io reference after 01/Dec/2023 -- macro: user_known_k8s_client_container - condition: > - (k8s.ns.name="kube-system" and container.image.repository in (user_known_k8s_ns_kube_system_images)) or container.image.repository in (user_known_k8s_images) - -- macro: user_known_k8s_client_container_parens - condition: (user_known_k8s_client_container) - -- rule: The docker client is executed in a container - desc: Detect a k8s client tool executed inside a container - condition: spawned_process and container and not user_known_k8s_client_container_parens and proc.name in (k8s_client_binaries) - output: "Docker or kubernetes client executed in container (user=%user.name user_loginuid=%user.loginuid %container.info parent=%proc.pname cmdline=%proc.cmdline pid=%proc.pid image=%container.image.repository:%container.image.tag)" + desc: > + Detect hardlink created over a curated list of sensitive files or subdirectories under /etc/ or + root directories. Can be customized as needed. Refer to further and equivalent guidance within the + rule "Read sensitive file untrusted". + condition: > + create_hardlink + and (evt.arg.oldpath in (sensitive_file_names)) + output: Hardlinks created over sensitive files (target=%evt.arg.oldpath linkpath=%evt.arg.newpath evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) priority: WARNING - tags: [container, mitre_execution, T1610] + tags: [maturity_stable, host, container, filesystem, mitre_credential_access, T1555] - list: user_known_packet_socket_binaries items: [] - rule: Packet socket created in container - desc: Detect new packet socket at the device driver (OSI Layer 2) level in a container. Packet socket could be used for ARP Spoofing and privilege escalation(CVE-2020-14386) by attacker. - condition: evt.type=socket and evt.arg[0] contains AF_PACKET and container and not proc.name in (user_known_packet_socket_binaries) - output: Packet socket was created in a container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid socket_info=%evt.args container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag) + desc: > + Detect new packet socket at the device driver (OSI Layer 2) level in a container. Packet socket could be used for ARP Spoofing + and privilege escalation (CVE-2020-14386) by an attacker. Noise can be reduced by using the user_known_packet_socket_binaries + template list. + condition: > + evt.type=socket and evt.dir=> + and container + and evt.arg.domain contains AF_PACKET + and not proc.name in (user_known_packet_socket_binaries) + output: Packet socket was created in a container (socket_info=%evt.args connection=%fd.name lport=%fd.lport rport=%fd.rport fd_type=%fd.type fd_proto=fd.l4proto evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) priority: NOTICE - tags: [container, network, mitre_discovery, T1046] - -# Namespaces where the rule is enforce -- list: namespace_scope_network_only_subnet - items: [] - -- macro: network_local_subnet - condition: > - fd.rnet in (rfc_1918_addresses) or - fd.ip = "0.0.0.0" or - fd.net = "127.0.0.0/8" - -# # The rule is disabled by default. -# # How to test: -# # Add 'default' to namespace_scope_network_only_subnet -# # Run: -# kubectl run --generator=run-pod/v1 -n default -i --tty busybox --image=busybox --rm -- wget google.com -O /var/google.html -# # Check logs running - -- rule: Network Connection outside Local Subnet - desc: Detect traffic to image outside local subnet. - condition: > - inbound_outbound and - container and - not network_local_subnet and - k8s.ns.name in (namespace_scope_network_only_subnet) - enabled: false - output: > - Network connection outside local subnet - (command=%proc.cmdline pid=%proc.pid connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id - image=%container.image.repository namespace=%k8s.ns.name - fd.rip.name=%fd.rip.name fd.lip.name=%fd.lip.name fd.cip.name=%fd.cip.name fd.sip.name=%fd.sip.name) - priority: WARNING - tags: [container, network, mitre_discovery, T1046] - -- list: allowed_image - items: [] # add image to monitor, i.e.: bitnami/nginx - -- list: authorized_server_binary - items: [] # add binary to allow, i.e.: nginx - -- list: authorized_server_port - items: [] # add port to allow, i.e.: 80 - -# # How to test: -# kubectl run --image=nginx nginx-app --port=80 --env="DOMAIN=cluster" -# kubectl expose deployment nginx-app --port=80 --name=nginx-http --type=LoadBalancer -# # On minikube: -# minikube service nginx-http -# # On general K8s: -# kubectl get services -# kubectl cluster-info -# # Visit the Nginx service and port, should not fire. -# # Change rule to different port, then different process name, and test again that it fires. - -- rule: Outbound or Inbound Traffic not to Authorized Server Process and Port - desc: Detect traffic that is not to authorized server process and port. - condition: > - inbound_outbound and - container and - container.image.repository in (allowed_image) and - not proc.name in (authorized_server_binary) and - not fd.sport in (authorized_server_port) - enabled: false - output: > - Network connection outside authorized port and binary - (command=%proc.cmdline pid=%proc.pid connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id - image=%container.image.repository) - priority: WARNING - tags: [container, network, mitre_discovery, TA0011] + tags: [maturity_stable, container, network, mitre_credential_access, T1557.002] - macro: user_known_stand_streams_redirect_activities condition: (never_true) -- macro: dup - condition: evt.type in (dup, dup2, dup3) - +# As of engine version 20 this rule can be improved by using the fd.types[] +# field so it only triggers once when all three of std{out,err,in} are +# redirected. +# +# - list: ip_sockets +# items: ["ipv4", "ipv6"] +# +# - rule: Redirect STDOUT/STDIN to Network Connection in Container once +# condition: dup and container and evt.rawres in (0, 1, 2) and fd.type in (ip_sockets) and fd.types[0] in (ip_sockets) and fd.types[1] in (ip_sockets) and fd.types[2] in (ip_sockets) and not user_known_stand_streams_redirect_activities +# +# The following rule has not been changed by default as existing users could be +# relying on the rule triggering when any of std{out,err,in} are redirected. - rule: Redirect STDOUT/STDIN to Network Connection in Container - desc: Detect redirecting stdout/stdin to network connection in container (potential reverse shell). - condition: dup and container and evt.rawres in (0, 1, 2) and fd.type in ("ipv4", "ipv6") and not user_known_stand_streams_redirect_activities - output: > - Redirect stdout/stdin to network connection (user=%user.name user_loginuid=%user.loginuid %container.info process=%proc.name parent=%proc.pname cmdline=%proc.cmdline pid=%proc.pid terminal=%proc.tty container_id=%container.id image=%container.image.repository fd.name=%fd.name fd.num=%fd.num fd.type=%fd.type fd.sip=%fd.sip) + desc: > + Detect redirection of stdout/stdin to a network connection within a container, achieved by utilizing a + variant of the dup syscall (potential reverse shell or remote code execution + https://github.com/swisskyrepo/PayloadsAllTheThings/). This detection is behavior-based and may generate + noise in the system, and can be adjusted using the user_known_stand_streams_redirect_activities template + macro. Tuning can be performed similarly to existing detections based on process lineage or container images, + and/or it can be limited to interactive tty (tty != 0). + condition: > + dup + and container + and evt.rawres in (0, 1, 2) + and fd.type in ("ipv4", "ipv6") + and not user_known_stand_streams_redirect_activities + output: Redirect stdout/stdin to network connection (gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] fd.sip=%fd.sip connection=%fd.name lport=%fd.lport rport=%fd.rport fd_type=%fd.type fd_proto=fd.l4proto evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) priority: NOTICE - tags: [container, network, process, mitre_discovery, mitre_execution, T1059] - -# The two Container Drift rules below will fire when a new executable is created in a container. -# There are two ways to create executables - file is created with execution permissions or permissions change of existing file. -# We will use a new filter, is_open_exec, to find all files creations with execution permission, and will trace all chmods in a container. -# The use case we are targeting here is an attempt to execute code that was not shipped as part of a container (drift) - -# an activity that might be malicious or non-compliant. -# Two things to pay attention to: -# 1) In most cases, 'docker cp' will not be identified, but the assumption is that if an attacker gained access to the container runtime daemon, they are already privileged -# 2) Drift rules will be noisy in environments in which containers are built (e.g. docker build) -# These two rules are not enabled by default. - -- macro: user_known_container_drift_activities - condition: (always_true) - -- rule: Container Drift Detected (chmod) - desc: New executable created in a container due to chmod - condition: > - chmod and - container and - not runc_writing_exec_fifo and - not runc_writing_var_lib_docker and - not user_known_container_drift_activities and - evt.rawres>=0 and - ((evt.arg.mode contains "S_IXUSR") or - (evt.arg.mode contains "S_IXGRP") or - (evt.arg.mode contains "S_IXOTH")) - enabled: false - output: Drift detected (chmod), new executable created in a container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid filename=%evt.arg.filename name=%evt.arg.name mode=%evt.arg.mode event=%evt.type) - priority: ERROR - tags: [container, process, filesystem, mitre_execution, T1059] - -# **************************************************************************** -# * "Container Drift Detected (open+create)" requires FALCO_ENGINE_VERSION 6 * -# **************************************************************************** -- rule: Container Drift Detected (open+create) - desc: New executable created in a container due to open+create - condition: > - evt.type in (open,openat,openat2,creat) and - evt.is_open_exec=true and - container and - not runc_writing_exec_fifo and - not runc_writing_var_lib_docker and - not user_known_container_drift_activities and - evt.rawres>=0 - enabled: false - output: Drift detected (open+create), new executable created in a container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid filename=%evt.arg.filename name=%evt.arg.name mode=%evt.arg.mode event=%evt.type) - priority: ERROR - tags: [container, process, filesystem, mitre_execution, T1059] - -- list: c2_server_ip_list - items: [] - -- list: c2_server_fqdn_list - items: [] - -# For example, you can fetch a list of IP addresses and FQDN on this website: -# https://feodotracker.abuse.ch/downloads/ipblocklist_recommended.json. -# Use Falco HELM chart to update (append) the c2 server lists with your values. -# See an example below. -# -# ```yaml -# # values.yaml Falco HELM chart file -# [...] -# customRules: -# c2-servers-list.yaml: |- -# - list: c2_server_ip_list -# append: true -# items: -# - "'51.178.161.32'" -# - "'46.101.90.205'" -# -# - list: c2_server_fqdn_list -# append: true -# items: -# - "srv-web.ffconsulting.com" -# - "57.ip-142-44-247.net" -# ``` - -- rule: Outbound Connection to C2 Servers - desc: Detect outbound connection to command & control servers thanks to a list of IP addresses & a list of FQDN. - condition: > - outbound and - ((fd.sip in (c2_server_ip_list)) or - (fd.sip.name in (c2_server_fqdn_list))) - output: Outbound connection to C2 server (c2_domain=%fd.sip.name c2_addr=%fd.sip command=%proc.cmdline connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id image=%container.image.repository) - priority: WARNING - enabled: false - tags: [host, container, network, mitre_command_and_control, TA0011] + tags: [maturity_stable, container, network, process, mitre_execution, T1059] - list: allowed_container_images_loading_kernel_module items: [] -# init_module and finit_module syscalls are available since Falco 0.35.0 -# rule coverage now extends to modprobe usage via init_module logging -# and previous alerting on spawned_process and insmod is now covered -# by finit_module syscall - rule: Linux Kernel Module Injection Detected - desc: Detect kernel module was injected (from container). - condition: kernel_module_load and container + desc: > + Inject Linux Kernel Modules from containers using insmod or modprobe with init_module and finit_module + syscalls, given the precondition of sys_module effective capabilities. Profile the environment and consider + allowed_container_images_loading_kernel_module to reduce noise and account for legitimate cases. + condition: > + kernel_module_load + and container + and thread.cap_effective icontains sys_module and not container.image.repository in (allowed_container_images_loading_kernel_module) - and thread.cap_effective icontains sys_module - output: Linux Kernel Module injection from container detected (user=%user.name uid=%user.uid user_loginuid=%user.loginuid process_name=%proc.name parent_process_name=%proc.pname parent_exepath=%proc.pexepath %proc.aname[2] %proc.aexepath[2] module=%proc.args %container.info image=%container.image.repository:%container.image.tag res=%evt.res syscall=%evt.type) + output: Linux Kernel Module injection from container (parent_exepath=%proc.pexepath gparent=%proc.aname[2] gexepath=%proc.aexepath[2] module=%proc.args res=%evt.res evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) priority: WARNING - tags: [host, container, process, mitre_execution, mitre_persistence, TA0002] - -- list: run_as_root_image_list - items: [] - -- macro: user_known_run_as_root_container - condition: (container.image.repository in (run_as_root_image_list)) - -# The rule is disabled by default and should be enabled when non-root container policy has been applied. -# Note the rule will not work as expected when usernamespace is applied, e.g. userns-remap is enabled. -- rule: Container Run as Root User - desc: Detected container running as root user - condition: spawned_process and container and proc.vpid=1 and user.uid=0 and not user_known_run_as_root_container - enabled: false - output: Container launched with root user privilege (uid=%user.uid container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag) - priority: INFO - tags: [container, process, users, mitre_execution, T1610] - -# This rule helps detect CVE-2021-3156: -# A privilege escalation to root through heap-based buffer overflow -- rule: Sudo Potential Privilege Escalation - desc: Privilege escalation vulnerability affecting sudo (<= 1.9.5p2). Executing sudo using sudoedit -s or sudoedit -i command with command-line argument that ends with a single backslash character from an unprivileged user it's possible to elevate the user privileges to root. - condition: spawned_process and user.uid != 0 and (proc.name=sudoedit or proc.name = sudo) and (proc.args contains -s or proc.args contains -i or proc.args contains --login) and (proc.args contains "\ " or proc.args endswith \) - output: "Detect Sudo Privilege Escalation Exploit (CVE-2021-3156) (user=%user.name parent=%proc.pname cmdline=%proc.cmdline pid=%proc.pid %container.info)" - priority: CRITICAL - tags: [host, container, filesystem, users, mitre_privilege_escalation, T1548.003] + tags: [maturity_stable, host, container, process, mitre_persistence, TA0003] - rule: Debugfs Launched in Privileged Container - desc: Detect file system debugger debugfs launched inside a privileged container which might lead to container escape. + desc: > + Detect file system debugger debugfs launched inside a privileged container which might lead to container escape. + This rule has a more narrow scope. condition: > - spawned_process and container + spawned_process + and container and container.privileged=true and proc.name=debugfs - output: Debugfs launched started in a privileged container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid %container.info image=%container.image.repository:%container.image.tag) + output: Debugfs launched started in a privileged container (evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) priority: WARNING - tags: [container, cis, process, mitre_execution, mitre_lateral_movement, T1611] - -- macro: mount_info - condition: (proc.args="" or proc.args intersects ("-V", "-l", "-h")) - -- macro: known_gke_mount_in_privileged_containers - condition: - (k8s.ns.name = kube-system - and container.image.repository = gke.gcr.io/gcp-compute-persistent-disk-csi-driver) - -- macro: user_known_mount_in_privileged_containers - condition: (never_true) - -- rule: Mount Launched in Privileged Container - desc: Detect file system mount happened inside a privileged container which might lead to container escape. - condition: > - spawned_process and container - and container.privileged=true - and proc.name=mount - and not mount_info - and not known_gke_mount_in_privileged_containers - and not user_known_mount_in_privileged_containers - output: Mount was executed inside a privileged container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid %container.info image=%container.image.repository:%container.image.tag) - priority: WARNING - tags: [container, cis, filesystem, mitre_lateral_movement, T1611] - -- list: user_known_userfaultfd_processes - items: [] - -- rule: Unprivileged Delegation of Page Faults Handling to a Userspace Process - desc: Detect a successful unprivileged userfaultfd syscall which might act as an attack primitive to exploit other bugs - condition: > - evt.type = userfaultfd and - user.uid != 0 and - (evt.rawres >= 0 or evt.res != -1) and - not proc.name in (user_known_userfaultfd_processes) - output: An userfaultfd syscall was successfully executed by an unprivileged user (user=%user.name user_loginuid=%user.loginuid process=%proc.name command=%proc.cmdline pid=%proc.pid %container.info image=%container.image.repository:%container.image.tag) - priority: CRITICAL - tags: [host, container, process, mitre_defense_evasion, TA0005] - -- list: ingress_remote_file_copy_binaries - items: [wget] - -- macro: ingress_remote_file_copy_procs - condition: (proc.name in (ingress_remote_file_copy_binaries)) - -# Users should overwrite this macro to specify conditions under which a -# Custom condition for use of ingress remote file copy tool in container -- macro: user_known_ingress_remote_file_copy_activities - condition: (never_true) - -- macro: curl_download - condition: proc.name = curl and - (proc.cmdline contains " -o " or - proc.cmdline contains " --output " or - proc.cmdline contains " -O " or - proc.cmdline contains " --remote-name ") - -- rule: Launch Ingress Remote File Copy Tools in Container - desc: Detect ingress remote file copy tools launched in container - condition: > - spawned_process and - container and - (ingress_remote_file_copy_procs or curl_download) and - not user_known_ingress_remote_file_copy_activities - output: > - Ingress remote file copy tool launched in container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid parent_process=%proc.pname - container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag) - priority: NOTICE - tags: [container, network, process, mitre_command_and_control, TA0011] - -# This rule helps detect CVE-2021-4034: -# A privilege escalation to root through memory corruption -- rule: Polkit Local Privilege Escalation Vulnerability (CVE-2021-4034) - desc: "This rule detects an attempt to exploit a privilege escalation vulnerability in Polkit's pkexec. By running specially crafted code, a local user can leverage this flaw to gain root privileges on a compromised system" - condition: - spawned_process and user.uid != 0 and proc.name=pkexec and proc.args = '' - output: - "Detect Polkit pkexec Local Privilege Escalation Exploit (CVE-2021-4034) (user=%user.loginname uid=%user.loginuid command=%proc.cmdline pid=%proc.pid args=%proc.args)" - priority: CRITICAL - tags: [host, container, process, users, mitre_privilege_escalation, TA0004] - + tags: [maturity_stable, container, cis, process, mitre_privilege_escalation, T1611] - rule: Detect release_agent File Container Escapes - desc: "This rule detect an attempt to exploit a container escape using release_agent file. By running a container with certains capabilities, a privileged user can modify release_agent file and escape from the container" - condition: - open_write and container and fd.name endswith release_agent and (user.uid=0 or thread.cap_effective contains CAP_DAC_OVERRIDE) and thread.cap_effective contains CAP_SYS_ADMIN - output: - "Detect an attempt to exploit a container escape using release_agent file (user=%user.name user_loginuid=%user.loginuid filename=%fd.name %container.info image=%container.image.repository:%container.image.tag cap_effective=%thread.cap_effective)" + desc: > + Detect an attempt to exploit a container escape using release_agent file. + By running a container with certains capabilities, a privileged user can modify + release_agent file and escape from the container. + condition: > + open_write + and container + and fd.name endswith release_agent + and (user.uid=0 or thread.cap_effective contains CAP_DAC_OVERRIDE) + and thread.cap_effective contains CAP_SYS_ADMIN + output: Detect an attempt to exploit a container escape using release_agent file (file=%fd.name cap_effective=%thread.cap_effective evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) priority: CRITICAL - tags: [container, process, mitre_privilege_escalation, mitre_lateral_movement, T1611] - -# Rule for detecting potential Log4Shell (CVE-2021-44228) exploitation -# Note: Not compatible with Java 17+, which uses read() syscalls -- macro: java_network_read - condition: (evt.type=recvfrom and fd.type in (ipv4, ipv6) and proc.name=java) - -- rule: Java Process Class File Download - desc: Detected Java process downloading a class file which could indicate a successful exploit of the log4shell Log4j vulnerability (CVE-2021-44228) - condition: > - java_network_read and evt.buffer bcontains cafebabe - output: Java process class file download (user=%user.name user_loginname=%user.loginname user_loginuid=%user.loginuid event=%evt.type connection=%fd.name server_ip=%fd.sip server_port=%fd.sport proto=%fd.l4proto process=%proc.name command=%proc.cmdline pid=%proc.pid parent=%proc.pname buffer=%evt.buffer container_id=%container.id image=%container.image.repository) - priority: CRITICAL - enabled: false - tags: [host, container, process, mitre_initial_access, T1190] + tags: [maturity_stable, container, process, mitre_privilege_escalation, T1611] - list: docker_binaries items: [docker, dockerd, containerd-shim, "runc:[1:CHILD]", pause, exe, docker-compose, docker-entrypoi, docker-runc-cur, docker-current, dockerd-current] -- macro: docker_procs - condition: proc.name in (docker_binaries) - -- rule: Modify Container Entrypoint - desc: This rule detect an attempt to write on container entrypoint symlink (/proc/self/exe). Possible CVE-2019-5736 Container Breakout exploitation attempt. - condition: > - open_write and container and (fd.name=/proc/self/exe or fd.name startswith /proc/self/fd/) and not docker_procs and not proc.cmdline = "runc:[1:CHILD] init" - enabled: false - output: > - Detect Potential Container Breakout Exploit (CVE-2019-5736) (user=%user.name process=%proc.name file=%fd.name cmdline=%proc.cmdline pid=%proc.pid %container.info) - priority: WARNING - tags: [container, filesystem, mitre_initial_access, T1611] - -- list: known_binaries_to_read_environment_variables_from_proc_files - items: [scsi_id, argoexec] - -- rule: Read environment variable from /proc files - desc: An attempt to read process environment variables from /proc files - condition: > - open_read and container and (fd.name glob /proc/*/environ) - and not proc.name in (known_binaries_to_read_environment_variables_from_proc_files) - output: > - Environment variables were retrieved from /proc files (user=%user.name user_loginuid=%user.loginuid program=%proc.name - command=%proc.cmdline pid=%proc.pid file=%fd.name parent=%proc.pname gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] container_id=%container.id image=%container.image.repository) - priority: WARNING - tags: [container, filesystem, process, mitre_credential_access, mitre_discovery, T1083] - - list: known_ptrace_binaries items: [] @@ -3348,23 +1130,40 @@ - macro: ptrace_attach_or_injection condition: > - evt.type=ptrace and evt.dir=> and + (evt.type=ptrace and evt.dir=> and (evt.arg.request contains PTRACE_POKETEXT or evt.arg.request contains PTRACE_POKEDATA or evt.arg.request contains PTRACE_ATTACH or evt.arg.request contains PTRACE_SEIZE or - evt.arg.request contains PTRACE_SETREGS) + evt.arg.request contains PTRACE_SETREGS)) - rule: PTRACE attached to process - desc: "This rule detects an attempt to inject code into a process using PTRACE." - condition: ptrace_attach_or_injection and proc_name_exists and not known_ptrace_procs - output: > - Detected ptrace PTRACE_ATTACH attempt (proc.cmdline=%proc.cmdline container=%container.info evt.type=%evt.type evt.arg.request=%evt.arg.request proc.pid=%proc.pid proc.cwd=%proc.cwd proc.ppid=%proc.ppid - proc.pcmdline=%proc.pcmdline proc.sid=%proc.sid proc.exepath=%proc.exepath user.uid=%user.uid user.loginuid=%user.loginuid user.loginname=%user.loginname user.name=%user.name group.gid=%group.gid - group.name=%group.name container.id=%container.id container.name=%container.name image=%container.image.repository) + desc: > + Detect an attempt to inject potentially malicious code into a process using PTRACE in order to evade + process-based defenses or elevate privileges. Common anti-patterns are debuggers. Additionally, profiling + your environment via the known_ptrace_procs template macro can reduce noise. + A successful ptrace syscall generates multiple logs at once. + condition: > + ptrace_attach_or_injection + and proc_name_exists + and not known_ptrace_procs + output: Detected ptrace PTRACE_ATTACH attempt (proc_pcmdline=%proc.pcmdline evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) priority: WARNING - tags: [host, container, process, mitre_execution, mitre_privilege_escalation, T1055.008] + tags: [maturity_stable, host, container, process, mitre_privilege_escalation, T1055.008] +- rule: PTRACE anti-debug attempt + desc: > + Detect usage of the PTRACE system call with the PTRACE_TRACEME argument, indicating a program actively attempting + to avoid debuggers attaching to the process. This behavior is typically indicative of malware activity. + Read more about PTRACE in the "PTRACE attached to process" rule. + condition: > + evt.type=ptrace and evt.dir=> + and evt.arg.request contains PTRACE_TRACEME + and proc_name_exists + output: Detected potential PTRACE_TRACEME anti-debug attempt (proc_pcmdline=%proc.pcmdline evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: NOTICE + tags: [maturity_stable, host, container, process, mitre_defense_evasion, T1622] + - macro: private_aws_credentials condition: > (proc.args icontains "aws_access_key_id" or @@ -3374,30 +1173,118 @@ proc.args icontains "secretaccesskey") - rule: Find AWS Credentials - desc: Find or grep AWS credentials - condition: > - spawned_process and - ((grep_commands and private_aws_credentials) or - (proc.name = "find" and proc.args endswith ".aws/credentials")) - output: Detected AWS credentials search activity (user.name=%user.name user.loginuid=%user.loginuid proc.cmdline=%proc.cmdline container.id=%container.id container_name=%container.name evt.type=%evt.type evt.res=%evt.res proc.pid=%proc.pid proc.cwd=%proc.cwd proc.ppid=%proc.ppid proc.pcmdline=%proc.pcmdline proc.sid=%proc.sid proc.exepath=%proc.exepath user.uid=%user.uid user.loginname=%user.loginname group.gid=%group.gid group.name=%group.name container.name=%container.name image=%container.image.repository:%container.image.tag) + desc: > + Detect attempts to search for private keys or passwords using the grep or find command, particularly targeting standard + AWS credential locations. This is often seen with unsophisticated attackers, as there are many ways to access files + using bash built-ins that could go unnoticed. Regardless, this serves as a solid baseline detection that can be tailored + to cover these gaps while maintaining an acceptable noise level. This rule complements the rule "Search Private Keys or Passwords". + condition: > + spawned_process + and ((grep_commands and private_aws_credentials) or + (proc.name = "find" and proc.args endswith ".aws/credentials")) + output: Detected AWS credentials search activity (proc_pcmdline=%proc.pcmdline proc_cwd=%proc.cwd group_gid=%group.gid group_name=%group.name user_loginname=%user.loginname evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) priority: WARNING - tags: [host, container, mitre_credential_access, process, aws, T1552] + tags: [maturity_stable, host, container, process, aws, mitre_credential_access, T1552] - rule: Execution from /dev/shm - desc: This rule detects file execution from the /dev/shm directory, a common tactic for threat actors to stash their readable+writable+(sometimes)executable files. - condition: > - spawned_process and - (proc.exe startswith "/dev/shm/" or - (proc.cwd startswith "/dev/shm/" and proc.exe startswith "./" ) or - (shell_procs and proc.args startswith "-c /dev/shm") or - (shell_procs and proc.args startswith "-i /dev/shm") or - (shell_procs and proc.args startswith "/dev/shm") or - (proc.cwd startswith "/dev/shm/" and proc.args startswith "./" )) and - not container.image.repository in (falco_privileged_images, trusted_images) - output: "File execution detected from /dev/shm (proc.cmdline=%proc.cmdline connection=%fd.name user.name=%user.name user.loginuid=%user.loginuid container.id=%container.id evt.type=%evt.type evt.res=%evt.res proc.pid=%proc.pid proc.cwd=%proc.cwd proc.ppid=%proc.ppid proc.pcmdline=%proc.pcmdline proc.sid=%proc.sid proc.exepath=%proc.exepath user.uid=%user.uid user.loginname=%user.loginname group.gid=%group.gid group.name=%group.name container.name=%container.name image=%container.image.repository)" + desc: > + This rule detects file execution in the /dev/shm directory, a tactic often used by threat actors to store their readable, writable, and + occasionally executable files. /dev/shm acts as a link to the host or other containers, creating vulnerabilities for their compromise + as well. Notably, /dev/shm remains unchanged even after a container restart. Consider this rule alongside the newer + "Drop and execute new binary in container" rule. + condition: > + spawned_process + and (proc.exe startswith "/dev/shm/" or + (proc.cwd startswith "/dev/shm/" and proc.exe startswith "./" ) or + (shell_procs and proc.args startswith "-c /dev/shm") or + (shell_procs and proc.args startswith "-i /dev/shm") or + (shell_procs and proc.args startswith "/dev/shm") or + (proc.cwd startswith "/dev/shm/" and proc.args startswith "./" )) + and not container.image.repository in (falco_privileged_images, trusted_images) + output: File execution detected from /dev/shm (evt_res=%evt.res file=%fd.name proc_cwd=%proc.cwd proc_pcmdline=%proc.pcmdline user_loginname=%user.loginname group_gid=%group.gid group_name=%group.name evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) priority: WARNING - tags: [host, container, mitre_execution, mitre_defense_evasion, T1036.005, T1059.004] + tags: [maturity_stable, host, container, mitre_execution, T1059.004] + +# List of allowed container images that are known to execute binaries not part of their base image. +- list: known_drop_and_execute_containers + items: [] + +- macro: known_drop_and_execute_activities + condition: (never_true) + +- rule: Drop and execute new binary in container + desc: > + Detect if an executable not belonging to the base image of a container is being executed. + The drop and execute pattern can be observed very often after an attacker gained an initial foothold. + is_exe_upper_layer filter field only applies for container runtimes that use overlayfs as union mount filesystem. + Adopters can utilize the provided template list known_drop_and_execute_containers containing allowed container + images known to execute binaries not included in their base image. Alternatively, you could exclude non-production + namespaces in Kubernetes settings by adjusting the rule further. This helps reduce noise by applying application + and environment-specific knowledge to this rule. Common anti-patterns include administrators or SREs performing + ad-hoc debugging. + condition: > + spawned_process + and container + and proc.is_exe_upper_layer=true + and not container.image.repository in (known_drop_and_execute_containers) + and not known_drop_and_execute_activities + output: Executing binary not part of base image (proc_exe=%proc.exe proc_sname=%proc.sname gparent=%proc.aname[2] proc_exe_ino_ctime=%proc.exe_ino.ctime proc_exe_ino_mtime=%proc.exe_ino.mtime proc_exe_ino_ctime_duration_proc_start=%proc.exe_ino.ctime_duration_proc_start proc_cwd=%proc.cwd container_start_ts=%container.start_ts evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) + priority: CRITICAL + tags: [maturity_stable, container, process, mitre_persistence, TA0003, PCI_DSS_11.5.1] + +# RFC1918 addresses were assigned for private network usage +- list: rfc_1918_addresses + items: ['"10.0.0.0/8"', '"172.16.0.0/12"', '"192.168.0.0/16"'] + +- macro: outbound + condition: > + (((evt.type = connect and evt.dir=<) or + (evt.type in (sendto,sendmsg) and evt.dir=< and + fd.l4proto != tcp and fd.connected=false and fd.name_changed=true)) and + (fd.typechar = 4 or fd.typechar = 6) and + (fd.ip != "0.0.0.0" and fd.net != "127.0.0.0/8" and not fd.snet in (rfc_1918_addresses)) and + (evt.rawres >= 0 or evt.res = EINPROGRESS)) + +- list: ssh_non_standard_ports + items: [80, 8080, 88, 443, 8443, 53, 4444] + +- macro: ssh_non_standard_ports_network + condition: (fd.sport in (ssh_non_standard_ports)) + +- rule: Disallowed SSH Connection Non Standard Port + desc: > + Detect any new outbound SSH connection from the host or container using a non-standard port. This rule holds the potential + to detect a family of reverse shells that cause the victim machine to connect back out over SSH, with STDIN piped from + the SSH connection to a shell's STDIN, and STDOUT of the shell piped back over SSH. Such an attack can be launched against + any app that is vulnerable to command injection. The upstream rule only covers a limited selection of non-standard ports. + We suggest adding more ports, potentially incorporating ranges based on your environment's knowledge and custom SSH port + configurations. This rule can complement the "Redirect STDOUT/STDIN to Network Connection in Container" or + "Disallowed SSH Connection" rule. + condition: > + outbound + and proc.exe endswith ssh + and fd.l4proto=tcp + and ssh_non_standard_ports_network + output: Disallowed SSH Connection (connection=%fd.name lport=%fd.lport rport=%fd.rport fd_type=%fd.type fd_proto=fd.l4proto evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty %container.info) + priority: NOTICE + tags: [maturity_stable, host, container, network, process, mitre_execution, T1059] -# Application rules have moved to application_rules.yaml. Please look -# there if you want to enable them by adding to -# falco_rules.local.yaml. +- list: known_memfd_execution_binaries + items: [] + +- macro: known_memfd_execution_processes + condition: (proc.name in (known_memfd_execution_binaries)) + +- rule: Fileless execution via memfd_create + desc: > + Detect if a binary is executed from memory using the memfd_create technique. This is a well-known defense evasion + technique for executing malware on a victim machine without storing the payload on disk and to avoid leaving traces + about what has been executed. Adopters can whitelist processes that may use fileless execution for benign purposes + by adding items to the list known_memfd_execution_processes. + condition: > + spawned_process + and proc.is_exe_from_memfd=true + and not known_memfd_execution_processes + output: Fileless execution via memfd_create (container_start_ts=%container.start_ts proc_cwd=%proc.cwd evt_res=%evt.res proc_sname=%proc.sname gparent=%proc.aname[2] evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info) + priority: CRITICAL + tags: [maturity_stable, host, container, process, mitre_defense_evasion, T1620] diff --git a/rules_inventory/rules_overview.md b/rules_inventory/rules_overview.md deleted file mode 100644 index e96d8fc87..000000000 --- a/rules_inventory/rules_overview.md +++ /dev/null @@ -1,255 +0,0 @@ - - - -# Falco Rules - Summary Stats - - - - - - -This document is auto-generated. Last Updated: 2023-01-23. - - -The Falco project ships with [78 default rules](https://github.com/falcosecurity/rules/blob/main/rules/falco_rules.yaml) around Linux syscalls and container events that were contributed by the community. - - -The intended outcome of this document is to provide a comprehensive overview of the default rules, provide additional resources and help drive future improvements. - - - - - -Falco default rules per workload type: - - - -| workload | rule_count | percentage | -|:----------------|-------------:|:-------------| -| container | 27 | 34.62% | -| container, host | 50 | 64.1% | -| host | 1 | 1.28% | - - - -Falco default rules per [Falco tag](https://falco.org/docs/rules/#tags): - - - -| extra_tag | rule_count | percentage | -|:--------------|-------------:|:-------------| -| aws | 2 | 1.94% | -| cis | 5 | 4.85% | -| database | 1 | 0.97% | -| filesystem | 30 | 29.13% | -| k8s | 2 | 1.94% | -| network | 22 | 21.36% | -| process | 28 | 27.18% | -| shell | 2 | 1.94% | -| software_mgmt | 3 | 2.91% | -| users | 8 | 7.77% | - - - -Falco default rules per [Mitre Attack](https://attack.mitre.org/) phase: - - - -| mitre_phase | rules | percentage | -|:---------------------------|:-----------------------------------------------------------------------|:-------------| -| mitre_command_and_control | Disallowed SSH Connection | 7.69% | -| | Launch Ingress Remote File Copy Tools in Container | | -| | Outbound Connection to C2 Servers | | -| | Program run with disallowed http proxy env | | -| | Unexpected inbound connection source | | -| | Unexpected outbound connection destination | | -| mitre_credential_access | Create Hardlink Over Sensitive Files | 10.26% | -| | Create Symlink Over Sensitive Files | | -| | Directory traversal monitored file read | | -| | Find AWS Credentials | | -| | Read environment variable from /proc files | | -| | Read sensitive file trusted after startup | | -| | Read sensitive file untrusted | | -| | Search Private Keys or Passwords | | -| mitre_defense_evasion | Clear Log Activities | 6.41% | -| | Delete Bash History | | -| | Delete or rename shell history | | -| | Execution from /dev/shm | | -| | Unprivileged Delegation of Page Faults Handling to a Userspace Process | | -| mitre_discovery | Contact EC2 Instance Metadata Service From Container | 17.95% | -| | Contact K8S API Server From Container | | -| | Contact cloud metadata service from container | | -| | Directory traversal monitored file read | | -| | Launch Suspicious Network Tool in Container | | -| | Launch Suspicious Network Tool on Host | | -| | Network Connection outside Local Subnet | | -| | Outbound or Inbound Traffic not to Authorized Server Process and Port | | -| | Packet socket created in container | | -| | Read Shell Configuration File | | -| | Read environment variable from /proc files | | -| | Read sensitive file untrusted | | -| | Read ssh information | | -| | Redirect STDOUT/STDIN to Network Connection in Container | | -| mitre_execution | Container Drift Detected (chmod) | 20.51% | -| | Container Drift Detected (open+create) | | -| | Container Run as Root User | | -| | DB program spawned process | | -| | Debugfs Launched in Privileged Container | | -| | Detect crypto miners using the Stratum protocol | | -| | Detect outbound connections to common miner pool ports | | -| | Execution from /dev/shm | | -| | Linux Kernel Module Injection Detected | | -| | Netcat Remote Code Execution in Container | | -| | PTRACE attached to process | | -| | Redirect STDOUT/STDIN to Network Connection in Container | | -| | Run shell untrusted | | -| | System user interactive | | -| | Terminal shell in container | | -| | The docker client is executed in a container | | -| mitre_exfiltration | Create Hardlink Over Sensitive Files | 12.82% | -| | Create Symlink Over Sensitive Files | | -| | Directory traversal monitored file read | | -| | Interpreted procs inbound network activity | | -| | Interpreted procs outbound network activity | | -| | Launch Remote File Copy Tools in Container | | -| | Launch Suspicious Network Tool in Container | | -| | Launch Suspicious Network Tool on Host | | -| | System procs network activity | | -| | Unexpected UDP Traffic | | -| mitre_initial_access | Java Process Class File Download | 2.56% | -| | Modify Container Entrypoint | | -| mitre_lateral_movement | Change thread namespace | 12.82% | -| | Debugfs Launched in Privileged Container | | -| | Detect release_agent File Container Escapes | | -| | Disallowed SSH Connection | | -| | Launch Disallowed Container | | -| | Launch Excessively Capable Container | | -| | Launch Privileged Container | | -| | Launch Remote File Copy Tools in Container | | -| | Launch Sensitive Mount Container | | -| | Mount Launched in Privileged Container | | -| mitre_persistence | Create Hidden Files or Directories | 23.08% | -| | Create files below dev | | -| | Launch Package Management Process in Container | | -| | Linux Kernel Module Injection Detected | | -| | Mkdir binary dirs | | -| | Modify Shell Configuration File | | -| | Modify binary dirs | | -| | Remove Bulk Data from Disk | | -| | Schedule Cron Jobs | | -| | Set Setuid or Setgid bit | | -| | Unexpected K8s NodePort Connection | | -| | Update Package Repository | | -| | User mgmt binaries | | -| | Write below binary dir | | -| | Write below etc | | -| | Write below monitored dir | | -| | Write below root | | -| | Write below rpm database | | -| mitre_privilege_escalation | Change thread namespace | 10.26% | -| | Detect release_agent File Container Escapes | | -| | Launch Excessively Capable Container | | -| | Launch Privileged Container | | -| | Non sudo setuid | | -| | PTRACE attached to process | | -| | Polkit Local Privilege Escalation Vulnerability (CVE-2021-4034) | | -| | Sudo Potential Privilege Escalation | | - - - -# Falco Rules - Detailed Overview - - - - - -57 Falco rules (73.08% of rules) are enabled by default: - - -| rule | desc | workload | mitre_phase | mitre_ttp | extra_tags | -|:-----------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------|:-------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------| -| Contact EC2 Instance Metadata Service From Container | Detect attempts to contact the EC2 Instance Metadata Service from a container | container | mitre_discovery | [T1565](https://attack.mitre.org/techniques/T1565) | network, aws | -| Contact K8S API Server From Container | Detect attempts to contact the K8S API Server from a container | container | mitre_discovery | [T1565](https://attack.mitre.org/techniques/T1565) | network, k8s | -| Debugfs Launched in Privileged Container | Detect file system debugger debugfs launched inside a privileged container which might lead to container escape. | container | mitre_execution, mitre_lateral_movement | [T1611](https://attack.mitre.org/techniques/T1611) | cis, process | -| Detect release_agent File Container Escapes | This rule detect an attempt to exploit a container escape using release_agent file. By running a container with certains capabilities, a privileged user can modify release_agent file and escape from the container | container | mitre_lateral_movement, mitre_privilege_escalation | [T1611](https://attack.mitre.org/techniques/T1611) | process | -| Launch Disallowed Container | Detect the initial process started by a container that is not in a list of allowed containers. | container | mitre_lateral_movement | [T1610](https://attack.mitre.org/techniques/T1610) | | -| Launch Excessively Capable Container | Detect container started with a powerful set of capabilities. Exceptions are made for known trusted images. | container | mitre_lateral_movement, mitre_privilege_escalation | [T1610](https://attack.mitre.org/techniques/T1610) | cis | -| Launch Ingress Remote File Copy Tools in Container | Detect ingress remote file copy tools launched in container | container | mitre_command_and_control | [TA0011](https://attack.mitre.org/tactics/TA0011) | network, process | -| Launch Package Management Process in Container | Package management process ran inside container | container | mitre_persistence | [T1505](https://attack.mitre.org/techniques/T1505) | process, software_mgmt | -| Launch Privileged Container | Detect the initial process started in a privileged container. Exceptions are made for known trusted images. | container | mitre_lateral_movement, mitre_privilege_escalation | [T1610](https://attack.mitre.org/techniques/T1610) | cis | -| Launch Remote File Copy Tools in Container | Detect remote file copy tools launched in container | container | mitre_exfiltration, mitre_lateral_movement | [T1020](https://attack.mitre.org/techniques/T1020), [T1210](https://attack.mitre.org/techniques/T1210) | network, process | -| Launch Sensitive Mount Container | Detect the initial process started by a container that has a mount from a sensitive host directory (i.e. /proc). Exceptions are made for known trusted images. | container | mitre_lateral_movement | [T1610](https://attack.mitre.org/techniques/T1610) | cis | -| Launch Suspicious Network Tool in Container | Detect network tools launched inside container | container | mitre_discovery, mitre_exfiltration | [T1046](https://attack.mitre.org/techniques/T1046), [T1595](https://attack.mitre.org/techniques/T1595) | network, process | -| Mount Launched in Privileged Container | Detect file system mount happened inside a privileged container which might lead to container escape. | container | mitre_lateral_movement | [T1611](https://attack.mitre.org/techniques/T1611) | cis, filesystem | -| Netcat Remote Code Execution in Container | Netcat Program runs inside container that allows remote code execution | container | mitre_execution | [T1059](https://attack.mitre.org/techniques/T1059) | network, process | -| Packet socket created in container | Detect new packet socket at the device driver (OSI Layer 2) level in a container. Packet socket could be used for ARP Spoofing and privilege escalation(CVE-2020-14386) by attacker. | container | mitre_discovery | [T1046](https://attack.mitre.org/techniques/T1046) | network | -| Read environment variable from /proc files | An attempt to read process environment variables from /proc files | container | mitre_credential_access, mitre_discovery | [T1083](https://attack.mitre.org/techniques/T1083) | filesystem, process | -| Redirect STDOUT/STDIN to Network Connection in Container | Detect redirecting stdout/stdin to network connection in container (potential reverse shell). | container | mitre_discovery, mitre_execution | [T1059](https://attack.mitre.org/techniques/T1059) | network, process | -| Terminal shell in container | A shell was used as the entrypoint/exec point into a container with an attached terminal. | container | mitre_execution | [T1059](https://attack.mitre.org/techniques/T1059) | shell | -| The docker client is executed in a container | Detect a k8s client tool executed inside a container | container | mitre_execution | [T1610](https://attack.mitre.org/techniques/T1610) | | -| Unexpected K8s NodePort Connection | Detect attempts to use K8s NodePorts from a container | container | mitre_persistence | [T1205.001](https://attack.mitre.org/techniques/T1205/001) | network, k8s | -| Clear Log Activities | Detect clearing of critical log files | container, host | mitre_defense_evasion | [T1070](https://attack.mitre.org/techniques/T1070) | filesystem | -| Create Hardlink Over Sensitive Files | Detect hardlink created over sensitive files | container, host | mitre_credential_access, mitre_exfiltration | [T1020](https://attack.mitre.org/techniques/T1020), [T1083](https://attack.mitre.org/techniques/T1083), [T1212](https://attack.mitre.org/techniques/T1212), [T1552](https://attack.mitre.org/techniques/T1552), [T1555](https://attack.mitre.org/techniques/T1555) | filesystem | -| Create Symlink Over Sensitive Files | Detect symlink created over sensitive files | container, host | mitre_credential_access, mitre_exfiltration | [T1020](https://attack.mitre.org/techniques/T1020), [T1083](https://attack.mitre.org/techniques/T1083), [T1212](https://attack.mitre.org/techniques/T1212), [T1552](https://attack.mitre.org/techniques/T1552), [T1555](https://attack.mitre.org/techniques/T1555) | filesystem | -| Create files below dev | creating any files below /dev other than known programs that manage devices. Some rootkits hide files in /dev. | container, host | mitre_persistence | [T1083](https://attack.mitre.org/techniques/T1083), [T1543](https://attack.mitre.org/techniques/T1543) | filesystem | -| DB program spawned process | a database-server related program spawned a new process other than itself. This shouldn\'t occur and is a follow on from some SQL injection attacks. | container, host | mitre_execution | [T1190](https://attack.mitre.org/techniques/T1190) | process, database | -| Delete Bash History | Detect bash history deletion | container, host | mitre_defense_evasion | [T1070](https://attack.mitre.org/techniques/T1070) | process, filesystem | -| Delete or rename shell history | Detect shell history deletion | container, host | mitre_defense_evasion | [T1070](https://attack.mitre.org/techniques/T1070) | process, filesystem | -| Detect crypto miners using the Stratum protocol | Miners typically specify the mining pool to connect to with a URI that begins with 'stratum+tcp' | container, host | mitre_execution | [T1496](https://attack.mitre.org/techniques/T1496) | process | -| Directory traversal monitored file read | Web applications can be vulnerable to directory traversal attacks that allow accessing files outside of the web app's root directory (e.g. Arbitrary File Read bugs). System directories like /etc are typically accessed via absolute paths. Access patterns outside of this (here path traversal) can be regarded as suspicious. This rule includes failed file open attempts. | container, host | mitre_credential_access, mitre_discovery, mitre_exfiltration | [T1020](https://attack.mitre.org/techniques/T1020), [T1083](https://attack.mitre.org/techniques/T1083), [T1212](https://attack.mitre.org/techniques/T1212), [T1552](https://attack.mitre.org/techniques/T1552), [T1555](https://attack.mitre.org/techniques/T1555) | filesystem | -| Execution from /dev/shm | This rule detects file execution from the /dev/shm directory, a common tactic for threat actors to stash their readable+writable+(sometimes)executable files. | container, host | mitre_defense_evasion, mitre_execution | [T1036.005](https://attack.mitre.org/techniques/T1036/005), [T1059.004](https://attack.mitre.org/techniques/T1059/004) | | -| Find AWS Credentials | Find or grep AWS credentials | container, host | mitre_credential_access | [T1552](https://attack.mitre.org/techniques/T1552) | process, aws | -| Java Process Class File Download | Detected Java process downloading a class file which could indicate a successful exploit of the log4shell Log4j vulnerability (CVE-2021-44228) | container, host | mitre_initial_access | [T1190](https://attack.mitre.org/techniques/T1190) | process | -| Linux Kernel Module Injection Detected | Detect kernel module was injected (from container). | container, host | mitre_execution, mitre_persistence | [TA0002](https://attack.mitre.org/tactics/TA0002) | process | -| Mkdir binary dirs | an attempt to create a directory below a set of binary directories. | container, host | mitre_persistence | [T1222.002](https://attack.mitre.org/techniques/T1222/002) | filesystem | -| Modify Shell Configuration File | Detect attempt to modify shell configuration files | container, host | mitre_persistence | [T1546.004](https://attack.mitre.org/techniques/T1546/004) | filesystem | -| Modify binary dirs | an attempt to modify any file below a set of binary directories. | container, host | mitre_persistence | [T1222.002](https://attack.mitre.org/techniques/T1222/002) | filesystem | -| Non sudo setuid | an attempt to change users by calling setuid. sudo/su are excluded. users "root" and "nobody" suing to itself are also excluded, as setuid calls typically involve dropping privileges. | container, host | mitre_privilege_escalation | [T1548.001](https://attack.mitre.org/techniques/T1548/001) | users | -| Outbound Connection to C2 Servers | Detect outbound connection to command & control servers thanks to a list of IP addresses & a list of FQDN. | container, host | mitre_command_and_control | [TA0011](https://attack.mitre.org/tactics/TA0011) | network | -| PTRACE attached to process | This rule detects an attempt to inject code into a process using PTRACE. | container, host | mitre_execution, mitre_privilege_escalation | [T1055.008](https://attack.mitre.org/techniques/T1055/008) | process | -| Polkit Local Privilege Escalation Vulnerability (CVE-2021-4034) | This rule detects an attempt to exploit a privilege escalation vulnerability in Polkit's pkexec. By running specially crafted code, a local user can leverage this flaw to gain root privileges on a compromised system | container, host | mitre_privilege_escalation | [TA0004](https://attack.mitre.org/tactics/TA0004) | process, users | -| Read sensitive file trusted after startup | an attempt to read any sensitive file (e.g. files containing user/password/authentication information) by a trusted program after startup. Trusted programs might read these files at startup to load initial state, but not afterwards. | container, host | mitre_credential_access | [T1020](https://attack.mitre.org/techniques/T1020), [T1083](https://attack.mitre.org/techniques/T1083), [T1212](https://attack.mitre.org/techniques/T1212), [T1552](https://attack.mitre.org/techniques/T1552), [T1555](https://attack.mitre.org/techniques/T1555) | filesystem | -| Read sensitive file untrusted | an attempt to read any sensitive file (e.g. files containing user/password/authentication information). Exceptions are made for known trusted programs. | container, host | mitre_credential_access, mitre_discovery | [T1020](https://attack.mitre.org/techniques/T1020), [T1083](https://attack.mitre.org/techniques/T1083), [T1212](https://attack.mitre.org/techniques/T1212), [T1552](https://attack.mitre.org/techniques/T1552), [T1555](https://attack.mitre.org/techniques/T1555) | filesystem | -| Remove Bulk Data from Disk | Detect process running to clear bulk data from disk | container, host | mitre_persistence | [T1485](https://attack.mitre.org/techniques/T1485) | process, filesystem | -| Run shell untrusted | an attempt to spawn a shell below a non-shell application. Specific applications are monitored. | container, host | mitre_execution | [T1059.004](https://attack.mitre.org/techniques/T1059/004) | process, shell | -| Search Private Keys or Passwords | Detect grep private keys or passwords activity. | container, host | mitre_credential_access | [T1552.001](https://attack.mitre.org/techniques/T1552/001) | process, filesystem | -| Sudo Potential Privilege Escalation | Privilege escalation vulnerability affecting sudo (<= 1.9.5p2). Executing sudo using sudoedit -s or sudoedit -i command with command-line argument that ends with a single backslash character from an unprivileged user it's possible to elevate the user privileges to root. | container, host | mitre_privilege_escalation | [T1548.003](https://attack.mitre.org/techniques/T1548/003) | filesystem, users | -| System procs network activity | any network activity performed by system binaries that are not expected to send or receive any network traffic | container, host | mitre_exfiltration | [T1059](https://attack.mitre.org/techniques/T1059), [TA0011](https://attack.mitre.org/tactics/TA0011) | network | -| System user interactive | an attempt to run interactive commands by a system (i.e. non-login) user | container, host | mitre_execution | [T1059](https://attack.mitre.org/techniques/T1059) | users | -| Unprivileged Delegation of Page Faults Handling to a Userspace Process | Detect a successful unprivileged userfaultfd syscall which might act as an attack primitive to exploit other bugs | container, host | mitre_defense_evasion | [TA0005](https://attack.mitre.org/tactics/TA0005) | process | -| Update Package Repository | Detect package repositories get updated | container, host | mitre_persistence | [T1072](https://attack.mitre.org/techniques/T1072) | filesystem | -| User mgmt binaries | activity by any programs that can manage users, passwords, or permissions. sudo and su are excluded. Activity in containers is also excluded--some containers create custom users on top of a base linux distribution at startup. Some innocuous command lines that don't actually change anything are excluded. | container, host | mitre_persistence | [T1098](https://attack.mitre.org/techniques/T1098), [T1543](https://attack.mitre.org/techniques/T1543) | users, software_mgmt | -| Write below binary dir | an attempt to write to any file below a set of binary directories | container, host | mitre_persistence | [T1543](https://attack.mitre.org/techniques/T1543) | filesystem | -| Write below etc | an attempt to write to any file below /etc | container, host | mitre_persistence | [T1098](https://attack.mitre.org/techniques/T1098) | filesystem | -| Write below monitored dir | an attempt to write to any file below a set of monitored directories | container, host | mitre_persistence | [T1543](https://attack.mitre.org/techniques/T1543) | filesystem | -| Write below root | an attempt to write to any file directly below / or /root | container, host | mitre_persistence | [TA0003](https://attack.mitre.org/tactics/TA0003) | filesystem | -| Write below rpm database | an attempt to write to the rpm database by any non-rpm related program | container, host | mitre_persistence | [T1072](https://attack.mitre.org/techniques/T1072) | filesystem, software_mgmt | -| Launch Suspicious Network Tool on Host | Detect network tools launched on the host | host | mitre_discovery, mitre_exfiltration | [T1046](https://attack.mitre.org/techniques/T1046), [T1595](https://attack.mitre.org/techniques/T1595) | network, process | - - -21 Falco rules (26.92% of rules) are *not* enabled by default: - - -| rule | desc | workload | mitre_phase | mitre_ttp | extra_tags | -|:----------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------|:---------------------------------------------------|:-------------------------------------------------------------------------------------------------------|:--------------------| -| Contact cloud metadata service from container | Detect attempts to contact the Cloud Instance Metadata Service from a container | container | mitre_discovery | [T1565](https://attack.mitre.org/techniques/T1565) | network | -| Container Drift Detected (chmod) | New executable created in a container due to chmod | container | mitre_execution | [T1059](https://attack.mitre.org/techniques/T1059) | process, filesystem | -| Container Drift Detected (open+create) | New executable created in a container due to open+create | container | mitre_execution | [T1059](https://attack.mitre.org/techniques/T1059) | process, filesystem | -| Container Run as Root User | Detected container running as root user | container | mitre_execution | [T1610](https://attack.mitre.org/techniques/T1610) | process, users | -| Modify Container Entrypoint | This rule detect an attempt to write on container entrypoint symlink (/proc/self/exe). Possible CVE-2019-5736 Container Breakout exploitation attempt. | container | mitre_initial_access | [T1611](https://attack.mitre.org/techniques/T1611) | filesystem | -| Network Connection outside Local Subnet | Detect traffic to image outside local subnet. | container | mitre_discovery | [T1046](https://attack.mitre.org/techniques/T1046) | network | -| Outbound or Inbound Traffic not to Authorized Server Process and Port | Detect traffic that is not to authorized server process and port. | container | mitre_discovery | [TA0011](https://attack.mitre.org/tactics/TA0011) | network | -| Change thread namespace | an attempt to change a program/thread\'s namespace (commonly done as a part of creating a container) by calling setns. | container, host | mitre_lateral_movement, mitre_privilege_escalation | [T1611](https://attack.mitre.org/techniques/T1611) | process | -| Create Hidden Files or Directories | Detect hidden files or directories created | container, host | mitre_persistence | [T1564.001](https://attack.mitre.org/techniques/T1564/001) | filesystem | -| Detect outbound connections to common miner pool ports | Miners typically connect to miner pools on common ports. | container, host | mitre_execution | [T1496](https://attack.mitre.org/techniques/T1496) | network | -| Disallowed SSH Connection | Detect any new ssh connection to a host other than those in an allowed group of hosts | container, host | mitre_command_and_control, mitre_lateral_movement | [T1021.004](https://attack.mitre.org/techniques/T1021/004) | network | -| Interpreted procs inbound network activity | Any inbound network activity performed by any interpreted program (perl, python, ruby, etc.) | container, host | mitre_exfiltration | [TA0011](https://attack.mitre.org/tactics/TA0011) | network | -| Interpreted procs outbound network activity | Any outbound network activity performed by any interpreted program (perl, python, ruby, etc.) | container, host | mitre_exfiltration | [TA0011](https://attack.mitre.org/tactics/TA0011) | network | -| Program run with disallowed http proxy env | An attempt to run a program with a disallowed HTTP_PROXY environment variable | container, host | mitre_command_and_control | [T1090](https://attack.mitre.org/techniques/T1090), [T1204](https://attack.mitre.org/techniques/T1204) | users | -| Read Shell Configuration File | Detect attempts to read shell configuration files by non-shell programs | container, host | mitre_discovery | [T1546.004](https://attack.mitre.org/techniques/T1546/004) | filesystem | -| Read ssh information | Any attempt to read files below ssh directories by non-ssh programs | container, host | mitre_discovery | [T1005](https://attack.mitre.org/techniques/T1005) | filesystem | -| Schedule Cron Jobs | Detect cron jobs scheduled | container, host | mitre_persistence | [T1053.003](https://attack.mitre.org/techniques/T1053/003) | filesystem | -| Set Setuid or Setgid bit | When the setuid or setgid bits are set for an application, this means that the application will run with the privileges of the owning user or group respectively. Detect setuid or setgid bits set via chmod | container, host | mitre_persistence | [T1548.001](https://attack.mitre.org/techniques/T1548/001) | process, users | -| Unexpected UDP Traffic | UDP traffic not on port 53 (DNS) or other commonly used ports | container, host | mitre_exfiltration | [TA0011](https://attack.mitre.org/tactics/TA0011) | network | -| Unexpected inbound connection source | Detect any inbound connection from a source outside of an allowed set of ips, networks, or domain names | container, host | mitre_command_and_control | [TA0011](https://attack.mitre.org/tactics/TA0011) | network | -| Unexpected outbound connection destination | Detect any outbound connection to a destination outside of an allowed set of ips, networks, or domain names | container, host | mitre_command_and_control | [TA0011](https://attack.mitre.org/tactics/TA0011) | network | diff --git a/rules_inventory/scripts/requirements.txt b/rules_inventory/scripts/requirements.txt deleted file mode 100644 index be1f461e2..000000000 --- a/rules_inventory/scripts/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -pandas -pyyaml -tabulate \ No newline at end of file diff --git a/rules_inventory/scripts/rules_overview_generator.py b/rules_inventory/scripts/rules_overview_generator.py deleted file mode 100644 index 00f21b3e3..000000000 --- a/rules_inventory/scripts/rules_overview_generator.py +++ /dev/null @@ -1,91 +0,0 @@ -import pandas as pd -import yaml -import argparse -import datetime - -""" -Usage: -python rules_inventory/scripts/rules_overview_generator.py --rules_file=rules/falco_rules.yaml -""" - -BASE_MITRE_URL_TECHNIQUE="https://attack.mitre.org/techniques/" -BASE_MITRE_URL_TACTIC="https://attack.mitre.org/tactics/" -COLUMNS=['rule', 'desc', 'workload', 'mitre_phase', 'mitre_ttp', 'extra_tags', 'extra_tags_list', 'mitre_phase_list', 'enabled'] - -def arg_parser(): - parser = argparse.ArgumentParser() - parser.add_argument('--rules_file', help='Path to falco rules yaml file') - return parser.parse_args() - -def rules_to_df(rules_file): - l = [] - with open(rules_file, 'r') as f: - items = yaml.safe_load(f) - for item in items: - if 'rule' in item and 'tags' in item: - if len(item['tags']) > 0: - item['workload'], item['mitre_phase'], item['mitre_ttp'], item['extra_tags'] = [], [], [], [] - for i in item['tags']: - if i in ['host', 'container']: - item['workload'].append(i) - elif i.startswith('mitre'): - item['mitre_phase'].append(i) - elif i.startswith('T'): - if i.startswith('TA'): - item['mitre_ttp'].append('[{}]({}{})'.format(i, BASE_MITRE_URL_TACTIC, i.replace('.', '/'))) - else: - item['mitre_ttp'].append('[{}]({}{})'.format(i, BASE_MITRE_URL_TECHNIQUE, i.replace('.', '/'))) - else: - item['extra_tags'].append(i) - item['workload'].sort() - item['mitre_phase'].sort() - item['mitre_ttp'].sort() - item['mitre_phase_list'] = item['mitre_phase'] - item['extra_tags_list'] = item['extra_tags'] - item['enabled'] = (item['enabled'] if 'enabled' in item else True) - l.append([', '.join(item[x]) if x in ['workload', 'mitre_ttp', 'extra_tags', 'mitre_phase'] else item[x] for x in COLUMNS]) - df = pd.DataFrame.from_records(l, columns=COLUMNS) - return df.sort_values(by=['workload','rule'], inplace=False) - -def print_markdown(df): - n_rules=len(df) - - print('\n\n\n# Falco Rules - Summary Stats\n\n\n') - print('\n\n\nThis document is auto-generated. Last Updated: {}.\n\n'.format(datetime.date.today())) - print('The Falco project ships with [{} default rules](https://github.com/falcosecurity/rules/blob/main/rules/falco_rules.yaml) around Linux syscalls and container events that were contributed by the community.\n\n'.format(n_rules)) - print('The intended outcome of this document is to provide a comprehensive overview of the default rules, provide additional resources and help drive future improvements.\n\n') - - - print('\n\n\nFalco default rules per workload type:\n\n\n') - df_stats1 = df.groupby('workload').agg(rule_count=('workload', 'count')) - df_stats1['percentage'] = round(100.0 * df_stats1['rule_count'] / df_stats1['rule_count'].sum(), 2).astype(str) + '%' - print(df_stats1.to_markdown(index=True)) - - print('\n\n\nFalco default rules per [Falco tag](https://falco.org/docs/rules/#tags):\n\n\n') - df_stats2 = df[['rule', 'extra_tags_list']].explode('extra_tags_list') - df_stats2.rename(columns={'extra_tags_list':'extra_tag'}, inplace=True) - df_stats2 = df_stats2.groupby('extra_tag').agg(rule_count=('extra_tag', 'count')) - df_stats2['percentage'] = round(100.0 * df_stats2['rule_count'] / df_stats2['rule_count'].sum(), 2).astype(str) + '%' - print(df_stats2.to_markdown(index=True)) - - print('\n\n\nFalco default rules per [Mitre Attack](https://attack.mitre.org/) phase:\n\n\n') - df_stats3 = df[['rule', 'mitre_phase_list']].explode('mitre_phase_list') - df_stats3.rename(columns={'mitre_phase_list':'mitre_phase'}, inplace=True) - df_stats3.sort_values(by=['mitre_phase','rule'], inplace=True) - df_stats3 = df_stats3.groupby("mitre_phase").agg({"rule": lambda x: ['\n'.join(list(x)), len(list(x))]}) - df_stats3['rules'] = df_stats3['rule'].apply(lambda x: x[0]) - df_stats3['percentage'] = df_stats3['rule'].apply(lambda x: round((100.0 * x[1] / n_rules), 2)).astype(str) + '%' - print(df_stats3.drop('rule', axis=1).to_markdown(index=True)) - - print('\n\n\n# Falco Rules - Detailed Overview\n\n\n') - df_stats4 = df.drop(['extra_tags_list', 'mitre_phase_list'], axis=1) - df_enabled = df_stats4[(df_stats4['enabled'] == True)].drop(['enabled'], axis=1) - df_disabled = df_stats4[(df_stats4['enabled'] == False)].drop(['enabled'], axis=1) - print('\n\n{} Falco rules ({:.2f}% of rules) are enabled by default:\n\n'.format(len(df_enabled), (100.0 * len(df_enabled) / n_rules))) - print(df_enabled.to_markdown(index=False)) - print('\n\n{} Falco rules ({:.2f}% of rules) are *not* enabled by default:\n\n'.format(len(df_disabled), (100.0 * len(df_disabled) / n_rules))) - print(df_disabled.to_markdown(index=False)) - -if __name__ == "__main__": - args_parsed = arg_parser() - print_markdown(rules_to_df(args_parsed.rules_file))