Compare commits

..

No commits in common. "main" and "okta-0.1.0" have entirely different histories.

442 changed files with 2756 additions and 53347 deletions

View File

@ -1,16 +0,0 @@
---
Language: Cpp
BasedOnStyle: LLVM
AccessModifierOffset: -8
BreakBeforeBraces: Allman
BreakConstructorInitializers: AfterColon
ColumnLimit: 80
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
DerivePointerAlignment: true
IndentWidth: 4
SortIncludes: Never
SpaceAfterTemplateKeyword: false
SpaceBeforeCtorInitializerColon: false
SpaceBeforeParens: Never
UseTab: Never

View File

@ -1,6 +1,6 @@
<!-- Thanks for sending a pull request! Here are some tips for you:
1. If this is your first time, please read our contributor guidelines in the [CONTRIBUTING.md](https://github.com/falcosecurity/.github/blob/main/CONTRIBUTING.md) file and learn how to compile Falco from source [here](https://falco.org/docs/source).
1. If this is your first time, please read our contributor guidelines in the [CONTRIBUTING.md](https://github.com/falcosecurity/.github/blob/master/CONTRIBUTING.md) file and learn how to compile Falco from source [here](https://falco.org/docs/source).
2. Please label this pull request according to what type of issue you are addressing.
3. Please add a release note!
4. If the PR is unfinished while opening it specify a wip in the title before the actual title, for example, "wip: my awesome feature"

View File

@ -1,67 +0,0 @@
#!/bin/bash
RULES_DIR=$1
CONFIG_FILE=$2
PLUGIN_NAME=$3
RESULT_FILE=$4
CHECKER_TOOL=$5
FALCO_DOCKER_IMAGE=$6
LATEST_TAG=$7
set -e pipefail
rm -f $RESULT_FILE
touch $RESULT_FILE
extra_flags=""
loaded_plugins="$(cat $CONFIG_FILE | grep 'library_path: ' | cut -d ':' -f 2 | xargs)"
for plugin_lib in $loaded_plugins; do
extra_flags="${extra_flags} -f /usr/share/falco/plugins/${plugin_lib}"
done
cur_branch=`git rev-parse HEAD`
echo Current branch is \"$cur_branch\"
echo Checking version for rules file in dir \"$RULES_DIR\"...
# Get the rules files and save them.
# We sort the rules files but first we remove the file extension.
rules_files=$(ls ${RULES_DIR}/* | while read -r line; do echo "${line%.yaml}"; done | sort)
# Add the extension to the files.
# Append the .yaml extension back to the sorted strings
rules_files=$(echo "${rules_files}" | sed 's/$/.yaml/')
echo Rule files found: ${rules_files}
# We save the current rules files before going back to the previous
# version.
prefix="tmp-"
for rules_file in ${rules_files}; do
new_file="${prefix}$(basename "$rules_file")"
echo "Copying rules file ${rules_file} to temporary file ${new_file}"
cp "$rules_file" "$new_file"
tmp_rules+=" $new_file"
done
git checkout tags/$LATEST_TAG
chmod +x $CHECKER_TOOL
$CHECKER_TOOL \
compare \
--falco-image=$FALCO_DOCKER_IMAGE \
-c $CONFIG_FILE \
-l ${rules_files} \
-r ${tmp_rules} \
${extra_flags} \
1>tmp_res.txt
git switch --detach $cur_branch
echo '##' $(basename $RULES_DIR) >> $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
rm -f ${tmp_rules}
rm -f tmp_res.txt

View File

@ -1,35 +0,0 @@
version: 2
updates:
- package-ecosystem: gomod
directories:
- ./build/*
- ./plugins/*
- ./shared/go/*/*
schedule:
interval: "weekly"
open-pull-requests-limit: 10
groups:
gomod:
update-types:
- "patch"
- package-ecosystem: cargo
directories:
- /plugins/*
schedule:
interval: "weekly"
open-pull-requests-limit: 10
groups:
cargo:
update-types:
- "minor"
- "patch"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
groups:
actions:
update-types:
- "minor"
- "patch"

View File

@ -1,77 +0,0 @@
#!/bin/bash
set -o errexit
set -o nounset
set -o pipefail
# Plugins for which we need to check if there exist as alternative plugin.
# If so, then we set them as a dependency. This is a must for rulesfiles
# that have multiple plugins that satisfy their requirements and the plugin we are
# checking is an alternative.
# It accepts a single value or coma separated values.
PLUGINS=$1
filtered_entries=()
# Extract plugins requirement from all files and save in a local file.
# Combine the sections from multiple files and save the output to file.
yq eval-all --no-doc '.[].required_plugin_versions | select(. != null and . != "")' ${@:2} > combined_requirements.yaml
# Remove duplicates from the top level.
yq eval-all --inplace 'unique_by(.name)' combined_requirements.yaml
#echo $(cat combined_requirements.yaml)
for YAML_FILE in "combined_requirements.yaml"; do
#echo "Processing file $YAML_FILE"
# Get the length of the entries list
length=$(yq eval '. | length' "$YAML_FILE")
# Iterate over each index in the entries list
for ((i = 0; i < length; i++)); do
# Access the entry by index using yq
entry=$(yq eval '.['"$i"']' "$YAML_FILE")
# Extract name and version from the entry
name=$(echo "$entry" | yq eval '.name' -)
version=$(echo "$entry" | yq eval '.version' -)
# If a plugin we are considering exists as an alternative of another one, then we just skip.
# This case could happen when we are processing multiple files and one of them overrides the
# plugin since it has some specific rules for that plugin.
to_be_skipped=false
for alternative in $(yq eval '.[].alternatives[].name' combined_requirements.yaml);do
if [[ "$alternative" == "$name" ]]; then
to_be_skipped=true
break
fi
done
if [ "$to_be_skipped" = true ];then
#echo "skipping plugin ${name} because already an alternative"
continue
fi
# Check if alternatives exist
alternatives=$(echo "$entry" | yq eval '.alternatives[]?')
if [ -n "$alternatives" ]; then
is_alternative=false
# Get the length of the alternatives list
alt_length=$(echo "$entry" | yq eval '.alternatives | length' -)
# Iterate over each alternative
for ((j = 0; j < alt_length; j++)); do
alt_entry=$(echo "$entry" | yq eval '.alternatives['"$j"']?' -)
alt_name=$(echo "$alt_entry" | yq eval '.name' -)
alt_version=$(echo "$alt_entry" | yq eval '.version' -)
# If our plugin is set as an alternative then we use it as a dependency.
if [[ " ${PLUGINS//,/ } " =~ " $alt_name " ]]; then
#echo "Preferring alternative plugin ${alt_name} over ${name}"
is_alternative=true
name=$alt_name
version=$alt_version
break
fi
done
fi
filtered_entries+=("$name:$version")
done
done
# Output the filtered entries
printf "%s\n" "${filtered_entries[@]}"

View File

@ -1,26 +0,0 @@
#!/bin/bash
PLUGIN=$1
set +e pipefail
echo Searching tag with prefix prefix \"${PLUGIN}-\"...
git fetch --tags origin
latest_tag=`git describe --match="${PLUGIN}-[0-9]*" --match="plugins/${PLUGIN}/v*" --abbrev=0 --tags`
set -e pipefail
latest_ver="0.0.0"
if [ -z "$latest_tag" ]
then
echo Not previous tag has been found
else
echo Most recent tag found is \"$latest_tag\"
if [[ "${latest_tag}" == "plugins/${PLUGIN}"* ]]; then
latest_ver="${latest_tag##*/v}"
else
latest_ver="${latest_tag##*-}"
fi
fi
echo Setting plugin version for "${PLUGIN}" to $latest_ver
echo "version=$latest_ver" >> $GITHUB_OUTPUT
echo "ref=${latest_tag}" >> $GITHUB_OUTPUT

View File

@ -1,39 +0,0 @@
#!/bin/bash
PLUGIN=$1
# set expected paths for plugins' config and rules files
rules_dir="$GITHUB_WORKSPACE/plugins/${PLUGIN}/rules"
config_file="$GITHUB_WORKSPACE/plugins/${PLUGIN}/falco.yaml"
# set paths into step outputs
echo "rules_dir=${rules_dir}" >> "$GITHUB_OUTPUT"
echo "config_file=${config_file}" >> "$GITHUB_OUTPUT"
# craft a default falco.yaml if no custom one is available
if [ ! -f "$config_file" ]; then
# we assume that the current plugin is always a dependency
deps="$PLUGIN"
# we collect all plugin dependencies across all plugin rulesets
# todo(jasondellaluce): find a way to avoid ignoring alternatives
if [ -d "$rules_dir" ]; then
rules_files=$(ls $rules_dir/*)
echo Extracting plugin dependencies from rules file "${rules_files}"...
rules_deps=$($GITHUB_WORKSPACE/.github/extract-plugins-deps-from-rulesfile.sh $PLUGIN $rules_files)
echo "${rules_deps}"
fi
mkdir -p $(echo $config_file | sed 's:[^/]*$::')
touch $config_file
echo "plugins:" >> $config_file
for dep in $rules_deps; do
dep=$(echo $dep | tr -d '"' | cut -d ':' -f 1)
echo " - name: ${dep}" >> $config_file
echo " library_path: lib${dep}.so" >> $config_file
done
fi
echo Using config file "${config_file}"
cat ${config_file}
echo ""

View File

@ -1,22 +0,0 @@
#!/bin/bash
falco_image=$1
checker_tool=$2
config_file=$3
rules_files=$4
# craft rules validation command
validation_flags=""
for rules_file in $rules_files; do
validation_flags="${validation_flags} -r ${rules_file}"
done
# append plugin files to validation command
configured_plugins="$(cat $config_file | grep 'library_path: ' | cut -d ':' -f 2 | xargs)"
for plugin_lib in $configured_plugins; do
validation_flags="${validation_flags} -f /usr/share/falco/plugins/${plugin_lib}"
done
chmod +x $checker_tool
echo $checker_tool validate -c $config_file $validation_flags
$checker_tool validate --falco-image=$falco_image -c $config_file $validation_flags

View File

@ -1,67 +0,0 @@
name: Build Plugins PR
on:
pull_request:
branches: [main]
# Checks if any concurrent jobs under the same pull request or branch are being executed
# NOTE: this will cancel every workflow that is being ran against a PR as group is just the github ref (without the workflow name)
concurrency:
group: ci-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
build-plugins:
uses: ./.github/workflows/reusable_build_packages.yaml
with:
makecommand: make packages -j4
suffix: ${{ github.event.number }}
secrets: inherit
get-changed-plugins:
uses: ./.github/workflows/reusable_get_changed_plugins.yaml
build-rules-tool:
needs: [get-changed-plugins]
if: needs.get-changed-plugins.outputs.changed-plugins != '[]' && needs.get-changed-plugins.outputs.changed-plugins != ''
uses: ./.github/workflows/reusable_build_rules_tool.yaml
with:
output: rules-checker
repository: falcosecurity/rules
validate-plugins:
needs: [build-plugins, get-changed-plugins, build-rules-tool]
if: needs.get-changed-plugins.outputs.changed-plugins != '[]' && needs.get-changed-plugins.outputs.changed-plugins != ''
strategy:
fail-fast: false
matrix:
plugin: ${{ fromJson(needs.get-changed-plugins.outputs.changed-plugins) }}
uses: ./.github/workflows/reusable_validate_plugins.yaml
with:
plugin: ${{ matrix.plugin }}
falcoctl-version: 0.11.0
falco-image: falcosecurity/falco:0.40.0
plugins-artifact: plugins-x86_64-${{ github.event.number }}.tar.gz
rules-checker: ./rules-checker
arch: x86_64
suggest-rules-version:
needs: [build-plugins, get-changed-plugins, build-rules-tool]
if: needs.get-changed-plugins.outputs.changed-plugins != '[]' && needs.get-changed-plugins.outputs.changed-plugins != ''
strategy:
fail-fast: false
matrix:
plugin: ${{ fromJson(needs.get-changed-plugins.outputs.changed-plugins) }}
uses: ./.github/workflows/reusable_suggest_rules_version.yaml
with:
plugin: ${{ matrix.plugin }}
falco-image: falcosecurity/falco:0.40.0
plugins-artifact: plugins-x86_64-${{ github.event.number }}.tar.gz
rules-checker: ./rules-checker
arch: x86_64
job-index: ${{ strategy.job-index }}
upload-pr-info:
needs: [suggest-rules-version]
if: needs.get-changed-plugins.outputs.changed-plugins != '[]' && needs.get-changed-plugins.outputs.changed-plugins != ''
uses: ./.github/workflows/reusable_upload_pr_info.yaml

View File

@ -9,23 +9,17 @@
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "Golang CodeQL"
name: "CodeQL"
on:
push:
branches: [ main ]
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
branches: [ master ]
schedule:
- cron: '28 11 * * 2'
# Checks if any concurrent jobs under the same pull request or branch are being executed
# NOTE: this will cancel every workflow that is being ran against a PR as group is just the github ref (without the workflow name)
concurrency:
group: codeql-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
analyze:
name: Analyze
@ -37,23 +31,25 @@ jobs:
strategy:
fail-fast: false
matrix:
language: [ 'cpp', 'go' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@v2
- name: Setup Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
uses: actions/setup-go@v2
with:
go-version: 1.21
go-version: 1.17.8
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4
uses: github/codeql-action/init@v1
with:
languages: go
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
@ -62,7 +58,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@ -76,4 +72,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4
uses: github/codeql-action/analyze@v1

View File

@ -1,171 +0,0 @@
name: Build and test container plugin
on:
pull_request:
branches: [ main ]
paths:
- 'plugins/container/**'
push:
branches: [ main ]
paths:
- 'plugins/container/**'
workflow_dispatch:
jobs:
build-others:
strategy:
fail-fast: false
matrix:
os: [ windows-latest, macos-latest ]
include:
- os: windows-latest
artifact-name: 'libcontainer-win'
artifact-path: 'plugins/container/container.dll'
- os: macos-latest
artifact-name: 'libcontainer-osx'
artifact-path: 'plugins/container/libcontainer.dylib'
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
submodules: 'recursive'
- name: Setup Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version-file: plugins/container/go-worker/go.mod
cache-dependency-path: plugins/container/go-worker/go.sum
- name: Build plugin library
working-directory: plugins/container
run: make
- name: Upload artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ matrix.artifact-name }}
path: ${{ matrix.artifact-path }}
build-linux:
name: build-linux-${{ matrix.arch }}
runs-on: ${{ (matrix.arch == 'arm64' && 'ubuntu-22.04-arm') || 'ubuntu-22.04' }}
strategy:
fail-fast: false
matrix:
arch: [ amd64, arm64 ]
container: golang:1.23-bullseye
steps:
- name: Install plugin deps
run: apt-get update && apt-get install -y --no-install-recommends zip unzip ninja-build
- name: Install updated cmake version ⛓️
run: |
curl -L -o /tmp/cmake.tar.gz https://github.com/Kitware/CMake/releases/download/v3.31.4/cmake-3.31.4-linux-$(uname -m).tar.gz
gzip -d /tmp/cmake.tar.gz
tar -xpf /tmp/cmake.tar --directory=/tmp
cp -R /tmp/cmake-3.31.4-linux-$(uname -m)/* /usr
rm -rf /tmp/cmake-3.31.4-linux-$(uname -m)
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
submodules: 'recursive'
- name: Safe directory
run: git config --global --add safe.directory $GITHUB_WORKSPACE
- name: Build plugin library
working-directory: plugins/container
run: make
- name: Upload artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: libcontainer-${{ matrix.arch }}
path: 'plugins/container/libcontainer.so'
test:
name: test-${{ matrix.arch }}
needs: [build-linux]
runs-on: ${{ (matrix.arch == 'arm64' && 'ubuntu-24.04-arm') || 'ubuntu-24.04' }}
strategy:
fail-fast: false
matrix:
arch: [ amd64, arm64 ]
steps:
# libbtrfs: needed by podman package - build dep.
- name: Install go test deps
run: sudo apt-get install -y --no-install-recommends libbtrfs-dev
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
submodules: 'recursive'
- name: Setup Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version-file: plugins/container/go-worker/go.mod
cache-dependency-path: plugins/container/go-worker/go.sum
- name: Build go-worker executable
working-directory: plugins/container
run: make -C go-worker exe
- name: Run tests
working-directory: plugins/container
run: |
systemctl --user start podman
make test
falco-tests:
needs: [build-linux]
name: falco-tests-${{ matrix.arch }}
runs-on: ${{ (matrix.arch == 'arm64' && 'ubuntu-22.04-arm') || 'ubuntu-22.04' }}
strategy:
fail-fast: false
matrix:
arch: [ amd64, arm64 ]
container:
image: falcosecurity/falco:master-debian
steps:
- name: Download library
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: libcontainer-${{ matrix.arch }}
path: /usr/share/falco/plugins/
- name: Run falcosecurity/testing Falco tests
uses: falcosecurity/testing@main
with:
test-falco: 'true'
test-falcoctl: 'false'
test-k8saudit: 'false'
test-dummy: 'false'
static: 'false'
test-drivers: 'false'
show-all: 'true'
sudo: ''
libs-tests:
needs: [build-linux]
uses: falcosecurity/libs/.github/workflows/reusable_e2e_tests.yaml@master
with:
container_plugin_artifact_name: 'libcontainer-amd64'
secrets: inherit
formatting-check:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Run clang-format style check
uses: jidicula/clang-format-action@4726374d1aa3c6aecf132e5197e498979588ebc8 #v4.15.0
with:
clang-format-version: '18'
check-path: plugins/container
exclude-regex: 'plugin_config_schema.h'

View File

@ -1,54 +0,0 @@
# 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: ["Build Plugins PR"]
types:
- completed
jobs:
upload:
runs-on: ubuntu-latest
if: github.event.workflow_run.event == 'pull_request'
steps:
- name: 'Download artifact'
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # 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@60a0d83039c74a4aee543508d2ffcb1c3799cdea # 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')
});

View File

@ -1,56 +0,0 @@
name: Build dummy_c plugin
on:
pull_request:
branches: [ main ]
paths:
- 'plugins/dummy_c/**'
push:
branches: [ main ]
paths:
- 'plugins/dummy_c/**'
workflow_dispatch:
# Checks if any concurrent jobs under the same pull request or branch are being executed
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
build:
name: build
runs-on: ubuntu-22.04
steps:
- name: Checkout ⤵️
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: Install deps ⛓️
run: |
sudo apt update -y
sudo apt install -y --no-install-recommends build-essential
- name: Initialize CodeQL
uses: github/codeql-action/init@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4
with:
languages: cpp
- name: Build dummy_c plugin 🏗️
run: |
cd plugins/dummy_c
make libdummy_c.so
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4
formatting-check:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Run clang-format style check
uses: jidicula/clang-format-action@4726374d1aa3c6aecf132e5197e498979588ebc8 #v4.15.0
with:
clang-format-version: '14'
check-path: plugins/dummy_c

View File

@ -1,72 +0,0 @@
name: Build K8smeta plugin
on:
pull_request:
branches: [ main ]
paths:
- 'plugins/k8smeta/**'
push:
branches: [ main ]
paths:
- 'plugins/k8smeta/**'
workflow_dispatch:
# Checks if any concurrent jobs under the same pull request or branch are being executed
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
build-and-test:
name: build-and-test
runs-on: ubuntu-22.04
steps:
- name: Checkout ⤵️
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version: '1.21'
check-latest: true
- name: Install deps ⛓️
run: |
sudo apt update -y
sudo apt install -y --no-install-recommends cmake build-essential autoconf libtool pkg-config
- name: Initialize CodeQL
uses: github/codeql-action/init@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4
with:
languages: cpp
- name: Build k8s meta plugin 🏗️
run: |
cd plugins/k8smeta
mkdir build
cd build && cmake -DCMAKE_BUILD_TYPE=Release ../
make k8smeta -j6
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4
- name: Build and run tests 🏎️
run: |
cd plugins/k8smeta/build
make build-server
make build-tests
make run-server &
make run-tests
formatting-check:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Run clang-format style check
uses: jidicula/clang-format-action@4726374d1aa3c6aecf132e5197e498979588ebc8 #v4.15.0
with:
clang-format-version: '14'
check-path: plugins/k8smeta

View File

@ -1,61 +0,0 @@
name: Update Plugins-dev
on:
push:
branches: [ main ]
workflow_dispatch:
# Checks if any concurrent jobs is already being executed for main and cancel it.
concurrency:
group: ci-main
cancel-in-progress: true
jobs:
build-plugins-dev:
uses: falcosecurity/plugins/.github/workflows/reusable_build_packages.yaml@main
with:
makecommand: make packages -j4
suffix: dev
secrets: inherit
get-changed-plugins:
uses: ./.github/workflows/reusable_get_changed_plugins.yaml
build-rules-tool:
needs: [get-changed-plugins]
if: needs.get-changed-plugins.outputs.changed-plugins != '[]' && needs.get-changed-plugins.outputs.changed-plugins != ''
uses: ./.github/workflows/reusable_build_rules_tool.yaml
with:
output: rules-checker
repository: falcosecurity/rules
validate-plugins:
needs: [build-plugins-dev, get-changed-plugins, build-rules-tool]
if: needs.get-changed-plugins.outputs.changed-plugins != '[]' && needs.get-changed-plugins.outputs.changed-plugins != ''
strategy:
fail-fast: false
matrix:
plugin: ${{ fromJson(needs.get-changed-plugins.outputs.changed-plugins) }}
uses: ./.github/workflows/reusable_validate_plugins.yaml
with:
plugin: ${{ matrix.plugin }}
falcoctl-version: 0.11.0
falco-image: falcosecurity/falco:0.40.0
plugins-artifact: plugins-x86_64-dev.tar.gz
rules-checker: ./rules-checker
arch: x86_64
publish-plugins-dev:
needs: [build-plugins-dev, validate-plugins]
uses: falcosecurity/plugins/.github/workflows/reusable_publish_packages.yaml@main
with:
suffix: dev
secrets: inherit
publish-oci-artifacts-dev:
needs: [ build-plugins-dev, validate-plugins ]
uses: ./.github/workflows/reusable-publish-oci-artifacts.yaml
with:
dev-tag: main
suffix: dev
secrets: inherit

View File

@ -1,43 +0,0 @@
name: Check registry.yaml
on:
pull_request:
branches: [main]
paths:
- "registry.yaml"
push:
branches: [main]
paths:
- "registry.yaml"
workflow_dispatch:
# Checks if any concurrent jobs under the same pull request or branch are being executed
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
check-registry:
name: check-registry
runs-on: ubuntu-22.04
steps:
- name: Checkout ⤵️
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version: "1.21"
check-latest: true
- name: Build registry artifact tool
working-directory: build/registry
run: make
# Formal validation only.
# When a new plugin is added and not yet released,
# its OCI repo has not been created yet, so we can't validate it.
- name: Verify the correctness of registry.yaml
working-directory: build/registry
run: ./bin/registry check ../../registry.yaml

View File

@ -1,70 +0,0 @@
name: Release Plugins
on:
push:
tags:
# All tags
- '**'
# Checks if any concurrent jobs is running for release CI and eventually cancel it.
concurrency:
group: ci-release-${{ github.ref }}
cancel-in-progress: true
jobs:
extract-info:
runs-on: ubuntu-latest
outputs:
package: ${{ steps.regex-match.outputs.group1 }}
steps:
- name: Validate tag
uses: actions-ecosystem/action-regex-match@9e6c4fb3d5e898f505be7a1fb6e7b0a278f6665b # v2.0.2
id: regex-match
with:
text: ${{ github.ref_name }}
regex: '^plugins\/([a-z0-9_-]+)\/v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$'
- name: Fail on invalid tag
if: steps.regex-match.outputs.match == ''
run: exit 1
build-plugins:
needs: extract-info
uses: falcosecurity/plugins/.github/workflows/reusable_build_packages.yaml@main
with:
makecommand: make release/${{ needs.extract-info.outputs.package }} -j4
suffix: stable
secrets: inherit
build-rules-tool:
needs: [extract-info]
uses: ./.github/workflows/reusable_build_rules_tool.yaml
with:
output: rules-checker
repository: falcosecurity/rules
validate-plugins:
needs: [extract-info, build-plugins, build-rules-tool]
uses: ./.github/workflows/reusable_validate_plugins.yaml
with:
plugin: ${{ needs.extract-info.outputs.package }}
falcoctl-version: 0.11.0
falco-image: falcosecurity/falco:0.40.0
plugins-artifact: plugins-x86_64-stable.tar.gz
rules-checker: ./rules-checker
arch: x86_64
publish-plugins:
needs: [build-plugins, validate-plugins]
uses: falcosecurity/plugins/.github/workflows/reusable_publish_packages.yaml@main
with:
suffix: stable
secrets: inherit
publish-oci-artifacts:
needs: [ build-plugins, validate-plugins ]
uses: ./.github/workflows/reusable-publish-oci-artifacts.yaml
with:
suffix: stable
secrets: inherit

View File

@ -1,98 +0,0 @@
on:
workflow_call:
inputs:
dev-tag:
description: The tag used for oci artifacts published from the main branch.
required: false
type: string
suffix:
description: Suffix for uploading packages (dev or stable)
required: true
type: string
jobs:
publish-oci-artifacts:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
outputs:
matrix: ${{ steps.oci_build.outputs.REGISTRY_UPDATE_STATUS }}
steps:
- name: Download x86_64 plugins
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: plugins-x86_64-${{ inputs.suffix }}.tar.gz
path: /tmp/plugins-x86_64
- name: Download aarch64 plugins
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: plugins-aarch64-${{ inputs.suffix }}.tar.gz
path: /tmp/plugins-aarch64
- name: Checkout Plugins
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: Setup Golang
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version: "^1.21"
- name: Build registry artifact tool
working-directory: build/registry
run: make
- name: Upload OCI artifacts to GitHub packages
id: oci_build
env:
REGISTRY: ghcr.io
REGISTRY_USER: ${{ github.repository_owner }}
REGISTRY_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO_GITHUB: https://github.com/${{ github.repository_owner }}/plugins.git
working-directory: build/registry
run: |
REGISTRY_UPDATE_STATUS=$(./bin/registry update-oci-registry \
../../registry.yaml \
--plugins-amd64-path /tmp/plugins-x86_64 \
--plugins-arm64-path /tmp/plugins-aarch64 \
--rulesfiles-path /tmp/plugins-x86_64 \
--dev-tag "${{ inputs.dev-tag }}"
)
echo "REGISTRY_UPDATE_STATUS=${REGISTRY_UPDATE_STATUS}" >> $GITHUB_OUTPUT
# Create signatures of the plugin artifacts as OCI artifacts
sign-oci-artifacts:
needs: [publish-oci-artifacts]
runs-on: ubuntu-latest
if: ${{ needs.publish-oci-artifacts.outputs.matrix != '[]' }}
strategy:
matrix:
value: ${{ fromJson(needs.publish-oci-artifacts.outputs.matrix) }}
permissions:
contents: read
id-token: write
packages: write
steps:
- name: Install Cosign
uses: sigstore/cosign-installer@d58896d6a1865668819e1d91763c7751a165e159 # v3.9.2
- run: cosign version
- name: Log into ghcr.io
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Sign the artifacts with GitHub OIDC Token
run: cosign sign --yes ${{ matrix.value.repository.ref }}@${{ matrix.value.artifact.digest }}

View File

@ -1,74 +0,0 @@
# This is a reusable workflow used by main and release CI
on:
workflow_call:
inputs:
makecommand:
description: Command used for make
required: true
type: string
suffix:
description: Suffix for uploading packages (dev or stable)
required: true
type: string
jobs:
build-packages:
name: build-packages-${{ matrix.arch }}
runs-on: ${{ (matrix.arch == 'aarch64' && 'ubuntu-22.04-arm') || 'ubuntu-22.04' }}
strategy:
matrix:
arch: [x86_64, aarch64]
# Upgrading to a newer debian version would make the build process generate
# binaries that require newer GLIBC version so we need to be based on bullseye for now
container: golang:1.23-bullseye
steps:
- name: Install deps
run: |
apt update
apt install -y --no-install-recommends awscli build-essential autoconf libelf-dev libtool autotools-dev \
automake zip unzip ninja-build wget lsb-release software-properties-common gnupg
- name: Install updated clang version ⛓️
run: |
wget https://apt.llvm.org/llvm.sh
chmod u+x llvm.sh
./llvm.sh 19
ln -s /usr/bin/clang-19 /usr/bin/clang
- name: Install updated cmake version ⛓️
run: |
curl -L -o /tmp/cmake.tar.gz https://github.com/Kitware/CMake/releases/download/v3.31.4/cmake-3.31.4-linux-$(uname -m).tar.gz
gzip -d /tmp/cmake.tar.gz
tar -xpf /tmp/cmake.tar --directory=/tmp
cp -R /tmp/cmake-3.31.4-linux-$(uname -m)/* /usr
rm -rf /tmp/cmake-3.31.4-linux-$(uname -m)
- name: Install Rust 🦀
uses: dtolnay/rust-toolchain@b3b07ba8b418998c39fb20f53e8b695cdcc8de1b # v1
with:
toolchain: "1.86.0"
- name: Install bpf-linker
run: |
cargo install bpf-linker@0.9.14
- name: Configure Rust cache
uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 #v2
- name: Checkout Plugins ⤵️
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
submodules: "recursive"
- name: Safe directory
run: git config --global --add safe.directory $GITHUB_WORKSPACE
- name: Run build 🏗️
run: ${{ inputs.makecommand }}
- name: Upload artifacts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: plugins-${{ matrix.arch }}-${{ inputs.suffix }}.tar.gz
path: output/*.tar.gz

View File

@ -1,43 +0,0 @@
# This is a reusable workflow used by main and release CI
on:
workflow_call:
inputs:
output:
description: Name of the output binary
required: false
default: rules-check
type: string
repository:
description: Falco rules repository
required: false
default: falcosecurity/rules
type: string
jobs:
build-rules-checker:
runs-on: ubuntu-latest
steps:
- name: Setup Golang
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version: "1.19.0"
- name: Checkout rules
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: ${{ inputs.repository }}
- name: Build checker tool
working-directory: build/checker
run: go build -o ${{ inputs.output }}
- name: Test checker tool
working-directory: build/checker
run: go test ./... -cover
- name: Upload artifacts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: rules-tool.tar.gz
path: build/checker/${{ inputs.output }}
retention-days: 1

View File

@ -1,46 +0,0 @@
# This is a reusable workflow used by main CI
on:
workflow_call:
outputs:
changed-plugins:
description: "A json-encoded array with the names of plugins to be used by the CI"
value: ${{ jobs.get-values.outputs.changed-plugins }}
jobs:
get-values:
runs-on: ubuntu-latest
outputs:
changed-plugins: ${{ steps.set-changed-plugins.outputs.changed-plugins }}
steps:
- name: Checkout rules
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Get changed files
id: changed-plugins
if: github.event_name == 'pull_request'
uses: Ana06/get-changed-files@25f79e676e7ea1868813e21465014798211fad8c # v2.3.0
with:
format: space-delimited
token: ${{ secrets.GITHUB_TOKEN }}
- name: Get changed plugins
id: set-changed-plugins
run: |
# if we skip changed-plugins because we're not in a pull-request,
# then we consider all the rules contained in the repo
all_files="${{ steps.changed-plugins.outputs.all }}"
values=""
if [ -z "$all_files" ]; then
values=$(ls plugins)
else
for changed_file in $all_files; do
if [[ ${changed_file} =~ ^plugins/.* ]]; then
plugindir=$(echo ${changed_file} | sed -e 's/^plugins//' | sed -E 's_(/[^/]+).*_\1_')
pluginname="${plugindir:1}"
if [[ ! $values =~ "$pluginname" ]]; then
values="${values}$pluginname"$'\n'
fi
fi
done
fi
echo "changed-plugins=$(echo "${values}" | jq -R -s -c 'split("\n")' | jq -c 'map(select(length > 0))')" >> $GITHUB_OUTPUT

View File

@ -1,45 +0,0 @@
# This is a reusable workflow used by main and release CI
on:
workflow_call:
inputs:
suffix:
description: Suffix for uploading packages (dev or stable)
required: true
type: string
permissions:
id-token: write
contents: read
env:
AWS_S3_BUCKET: falco-distribution
AWS_S3_PREFIX: plugins
AWS_S3_REGION: eu-west-1
jobs:
publish-packages:
runs-on: ubuntu-latest
steps:
- name: Download x86_64 plugins
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: plugins-x86_64-${{ inputs.suffix }}.tar.gz
path: /tmp/plugins-x86_64
- name: Download aarch64 plugins
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: plugins-aarch64-${{ inputs.suffix }}.tar.gz
path: /tmp/plugins-aarch64
- name: Configure AWS credentials 🔧⚙️
uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1
with:
role-to-assume: "arn:aws:iam::292999226676:role/github_actions-plugins-s3"
aws-region: ${{ env.AWS_S3_REGION }}
- name: Upload files to S3 ⬆️
run: |
for package in /tmp/plugins-*/*.tar.gz; do
aws s3 cp --no-progress $package s3://${{ env.AWS_S3_BUCKET}}/${{ env.AWS_S3_PREFIX }}/${{ inputs.suffix }}/
done

View File

@ -1,122 +0,0 @@
# This is a reusable workflow used by the PR CI
on:
workflow_call:
inputs:
plugin:
description: Name of the plugin that needs to be validated
required: true
type: string
falco-image:
description: Docker image of Falco to be used for validation
required: true
type: string
plugins-artifact:
description: Name of the plugin artifact containing the dev builds
required: true
type: string
rules-checker:
description: Path of the rules checker tool built from falcosecurity/rules
required: true
type: string
arch:
description: Architecture of the plugins artifacts (x86_64 or aarch64)
required: true
type: string
job-index:
description: If used in a matrix, the value of strategy.job-index
required: false
default: 0
type: number
jobs:
# note: we don't need anything else than x86_64 since we're validating rules
check-version:
if: github.event_name == 'pull_request' && inputs.arch == 'x86_64'
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Install system dependencies
run: sudo wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq && sudo chmod +x /usr/bin/yq
- name: Setup plugin config and rules
id: get-config
run: ./.github/setup-plugin-config-rules.sh ${{ inputs.plugin }}
- name: Get latest tag
id: get-tag
run: ./.github/get-latest-plugin-version.sh ${{ inputs.plugin }}
- name: Download rules tool
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: rules-tool.tar.gz
# note: here we're loading the locally-built plugins, whereas another
# solution would be to pull them with falcoctl. The flaw with this
# approach is that we load the same plugin for both the "old" and the
# "new" rulesets. The issue would be that the job would fail whenever
# the two rulesets depend on plugins with different majors.
# todo(jasondellaluce): fix this corner case in the future
- name: Download plugins
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: ${{ inputs.plugins-artifact }}
path: /tmp/plugins-${{ inputs.arch }}
- name: Extract plugins
run: |
for archive in /tmp/plugins-*/*.tar.gz; do
echo Extracting archive "$archive"...
mkdir -p tmpdir && pushd tmpdir
tar -xvf $archive
sudo mkdir -p /usr/share/falco/plugins
sudo cp -r *.so /usr/share/falco/plugins || true
popd && rm -fr tmpdir
done
- name: Compare changed files with previous versions
id: compare
if: steps.get-tag.outputs.version != '0.0.0'
run: |
rules_dir=${{ steps.get-config.outputs.rules_dir }}
if [ -d "$rules_dir" ]; then
./.github/compare-rule-files.sh \
"$rules_dir" \
${{ steps.get-config.outputs.config_file }} \
${{ inputs.plugin }} \
rule_result.txt \
${{ inputs.rules-checker }} \
${{ inputs.falco-image }} \
${{ steps.get-tag.outputs.ref }}
if [ -s rule_result.txt ]; then
if [ ! -s result.txt ]; then
touch result.txt
fi
cat rule_result.txt >> result.txt
fi
fi
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-${{ inputs.job-index }}
- name: Upload PR info as artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: steps.compare.outputs.comment_file != ''
with:
name: pr-${{ inputs.job-index }}
path: pr/
retention-days: 1

View File

@ -1,37 +0,0 @@
# This is a reusable workflow used by the PR CI
on:
workflow_call:
jobs:
upload-pr-info:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Download PR infos
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
path: tmp-artifacts
- name: Save PR info
run: |
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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: pr
path: pr/
retention-days: 1

View File

@ -1,226 +0,0 @@
# This is a reusable workflow used by main and release CI
on:
workflow_call:
inputs:
plugin:
description: Name of the plugin that needs to be validated
required: true
type: string
falco-image:
description: Docker image of Falco to be used for validation
required: true
type: string
falcoctl-version:
description: Version of falcoctl to be used for pulling artifacts
required: true
type: string
plugins-artifact:
description: Name of the plugin artifact containing the dev builds
required: true
type: string
rules-checker:
description: Path of the rules checker tool built from falcosecurity/rules
required: true
type: string
arch:
description: Architecture of the plugins artifacts (x86_64 or aarch64)
required: true
type: string
jobs:
# todo(jasondellaluce): support aarch64 too
validate-local:
if: inputs.arch == 'x86_64'
runs-on: ubuntu-latest
container: golang:1.18
env:
GOFLAGS: "-buildvcs=false"
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Install system dependencies
run: wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq && chmod +x /usr/bin/yq
- name: Setup plugin config and rules
id: get-config
run: ./.github/setup-plugin-config-rules.sh ${{ inputs.plugin }}
- name: Download rules tool
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: rules-tool.tar.gz
- name: Download plugins
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: ${{ inputs.plugins-artifact }}
path: /tmp/plugins-${{ inputs.arch }}
- name: Install plugin and rules
run: |
set -e pipefail
arch=${{ inputs.arch }}
loaded_plugins="$(cat ${{ steps.get-config.outputs.config_file }} | grep '\- name: ' | cut -d ':' -f 2 | xargs)"
mkdir -p /etc/falco/falco
mkdir -p /usr/share/falco/plugins
# avoids git exit status 128: detected dubious ownership in repository
git config --global --add safe.directory $(pwd)
for plugin_name in $loaded_plugins; do
echo Installing locally-built plugin "$plugin_name"...
# At release time we only build the released plugin, so it's possible
# that validation requires a plugin that we haven't built locally.
# in those cases, we build it on-the-fly perform validation with it.
set +e pipefail
packages=$(ls /tmp/plugins-${arch}/${plugin_name}-* || echo "")
set -e pipefail
if [ -z "$packages" ]; then
echo Building plugin "$plugin_name" temporary packages...
make package/$plugin_name -j4
packages=$(ls $(pwd)/output/${plugin_name}-*)
fi
for archive in $packages; do
echo Extracting archive "$archive"...
mkdir -p tmpdir && cd tmpdir
tar -xvf $archive
cp -r *.yaml /etc/falco/falco || true
cp -r *.so /usr/share/falco/plugins || true
cd .. && rm -fr tmpdir
done
done
- name: Validate plugin and rules
run: |
# craft an empty rules file if none is available.
# this ensures that the plugin gets still loaded even if it has no rules.
rules_files=""
if [ ! -d "${{ steps.get-config.outputs.rules_dir }}" ]; then
touch tmp_rules.yaml
rules_files="./tmp_rules.yaml"
else
rules_files=$(ls ${{ steps.get-config.outputs.rules_dir }}/*)
fi
./.github/validate-rules.sh \
"${{ inputs.falco-image }}" \
"${{ inputs.rules-checker }}" \
"${{ steps.get-config.outputs.config_file }}" \
"$rules_files"
# todo(jasondellaluce): support aarch64 too
validate-falcoctl:
if: inputs.arch == 'x86_64'
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Install system dependencies
run: sudo wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq && sudo chmod +x /usr/bin/yq
- name: Setup plugin config and rules
id: get-config
run: ./.github/setup-plugin-config-rules.sh ${{ inputs.plugin }}
- name: Download plugins
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: ${{ inputs.plugins-artifact }}
path: /tmp/plugins-${{ inputs.arch }}
- name: Download rules tool
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: rules-tool.tar.gz
- name: Download falcoctl
run: |
curl --fail -LS "https://github.com/falcosecurity/falcoctl/releases/download/v${{ inputs.falcoctl-version }}/falcoctl_${{ inputs.falcoctl-version }}_linux_${{ inputs.arch == 'x86_64' && 'amd64' || 'arm64' }}.tar.gz" | tar -xz
sudo install -o root -g root -m 0755 falcoctl /usr/local/bin/falcoctl
# note(jsondellaluce): exploring the set of all dependencies including their
# alternatives and all the possible combinations of different versions would
# result in a combinatorial explosion. As such, we take the simple route
# of exploring a subset of all the possible combinations (which does not
# include the deps alternatives) that attempts resembling real-world use cases.
#
# We validate each ruleset by loading its plugin dependencies at different
# versions, with the following logic:
# - for all the plugin dependencies defined in the ruleset:
# - we take the plugin version of the dependency
# - we set the patch version number to 0, to forbid patch-level dependencies
# (e.g. falco will fail the validation if v0.8.1 of a plugin is required and we provide v0.8.0)
# - iteratively:
# - for all the plugin dependencies defined in the ruleset:
# - we download the plugin at the given version of the dependency by using falcoctl
# - we increase the minor version number of the plugin by 1
# - we validate the ruleset with Falco and stop if we encounter an error
# - if falcoctl didn't find any updated plugin with the increased minor
# version for any of the plugin deps, we stop iterating
#
# todo(jasondellaluce): improve this by attempting more cases
# todo(jasondellaluce): if we skip one minor version (e.g. bump from v0.1.0
# to v0.3.0), this algorithm would stop before finishing the exploration
- name: Validate plugin and rules
run: |
set -e pipefail
if [ ! -d "${{ steps.get-config.outputs.rules_dir }}" ]; then
exit 0
fi
sudo mkdir -p /usr/share/falco/plugins
rules_files=$(ls ${{ steps.get-config.outputs.rules_dir }}/*)
deps=$(./.github/extract-plugins-deps-from-rulesfile.sh \
"${{ inputs.plugin }}" \
"$rules_files")
echo "Deps: ${deps}"
ver_diff=0
has_updates=1
while [ "$has_updates" -eq 1 ]; do
has_updates=0
for dep in $deps; do
echo "Plugin: ${dep}"
plugin_name=$(echo $dep | tr -d '"' | cut -d ':' -f 1)
# forcing zero patch version to forbid patch-like dependencies
# bumping minor version at every iteration
plugin_ver=$(echo $dep | tr -d '"' | cut -d ':' -f 2)
plugin_ver_major=$(echo $plugin_ver | cut -d '.' -f 1)
plugin_ver_minor=$(expr $(echo $plugin_ver | cut -d '.' -f 2) + $ver_diff)
plugin_ver_patch=0
plugin_ver="${plugin_ver_major}.${plugin_ver_minor}.${plugin_ver_patch}"
set +e pipefail
sudo falcoctl artifact install ${plugin_name}:${plugin_ver}
if [ $? -eq 0 ]; then
echo Installed plugin "${plugin_name}" at version "${plugin_ver}"
has_updates=1
else
echo Can\'t pull plugin "${plugin_name}" at version "${plugin_ver}"
echo Attempt installing locally-built plugin "${plugin_name}"...
for archive in $(ls /tmp/plugins-${{ inputs.arch }}/${plugin_name}-*); do
echo Extracting archive "$archive"...
mkdir -p tmpdir && pushd tmpdir
tar -xvf $archive
sudo cp -r *.so /usr/share/falco/plugins || true
popd && rm -fr tmpdir
done
fi
set -e pipefail
done
ver_diff=$(expr $ver_diff + 1)
./.github/validate-rules.sh \
"${{ inputs.falco-image }}" \
"${{ inputs.rules-checker }}" \
"${{ steps.get-config.outputs.config_file }}" \
"$rules_files"
done

5
.gitignore vendored
View File

@ -1,10 +1,7 @@
*~
.vscode
.DS_Store
.idea
output/
plugins/*/*.so
plugins/*/lib*.h
plugins/dummy_c/nlohmann
plugins/dummy_c/include
plugins/dummy_c/*.tar.gz
plugins/dummy_c/sdk

View File

@ -1,30 +0,0 @@
repos:
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: 'v18.1.3'
hooks:
- id: clang-format
name: clang-format-18
files: ^plugins/container/.*$
exclude: plugin_config_schema\.h
stages: [pre-commit]
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: 'v14.0.6'
hooks:
- id: clang-format
name: clang-format-14
files: ^plugins/(k8smeta|dummy_c)/.*$
stages: [pre-commit]
- repo: local
hooks:
- id: rust-fmt
name: rust-fmt
description: Format files with rustfmt.
entry: rustfmt +nightly --color always
types: [rust]
language: system
stages: [pre-commit]
- id: dco
name: dco
entry: ./tools/local_hooks/dco-pre-commit-msg.sh
language: script
stages: [prepare-commit-msg]

121
Makefile
View File

@ -1,6 +1,5 @@
# SPDX-License-Identifier: Apache-2.0
#
# Copyright (C) 2023 The Falco Authors.
# Copyright (C) 2022 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
@ -15,8 +14,9 @@
SHELL := /bin/bash
GO ?= $(shell which go)
FALCOSECURITY_LIBS_REVISION=e25e44b3ba4cb90ba9ac75bf747978e41fb6b221
DEBUG = 1
PRE_RELEASE = --pre-release
OUTPUT_DIR := output
SOURCE_DIR := plugins
ARCH ?=$(shell uname -m)
@ -24,31 +24,18 @@ PLATFORM ?=$(shell uname -s | tr '[:upper:]' '[:lower:]')
plugins = $(shell ls -d ${SOURCE_DIR}/*/ | cut -f2 -d'/' | xargs)
plugins-clean = $(addprefix clean/,$(plugins))
plugins-changelogs = $(addprefix changelog/,$(plugins))
plugins-packages = $(addprefix package/,$(plugins))
plugins-releases = $(addprefix release/,$(plugins))
plugins-tidy = $(addprefix tidy/,$(plugins))
.PHONY: all
all: check-registry $(plugins)
.PHONY: $(plugins)
$(plugins): build/readme/readme
+cd plugins/$@ && make DEBUG=$(DEBUG)
# make rules, if any
+@cd plugins/$@ \
&& make rules \
&& echo "$@ rules generated" || :
# make readme, if any
+@cd plugins/$@ \
&& make readme READMETOOL=../../build/readme/bin/readme \
&& echo "$@ readme generated" || :
tidy/%:
+cd plugins/$@ && [-f go.mod] && $(GO) mod tidy
$(plugins):
cd plugins/$@ && make DEBUG=$(DEBUG)
.PHONY: clean
clean: $(plugins-clean) clean/packages clean/build/utils/version clean/build/registry/registry clean/build/changelog/changelog clean/build/readme/readme
clean: $(plugins-clean) clean/packages clean/build/utils/version clean/build/registry/registry
.PHONY: clean/packages
clean/packages:
@ -56,101 +43,67 @@ clean/packages:
.PHONY: $(plugins-clean)
$(plugins-clean):
+cd plugins/$(shell basename $@) && make clean
.PHONY: $(plugins-tidy)
$(plugins-tidy):
+cd plugins/$(shell basename $@) && [ -f go.mod ] && $(GO) mod tidy || true
.PHONY: tidy
tidy: $(plugins-tidy)
+cd build/utils && $(GO) mod tidy
+cd build/readme && $(GO) mod tidy
+cd build/registry && $(GO) mod tidy
+cd build/utils && $(GO) mod tidy
cd plugins/$(shell basename $@) && make clean
.PHONY: packages
packages: clean/packages $(plugins-packages)
packages: clean/packages $(plugins-clean) $(plugins-packages)
package/%: clean/% % build/utils/version
.PHONY: releases
releases: $(plugins-releases)
.PHONY: $(plugins-packages)
$(plugins-packages): all build/utils/version
$(eval PLUGIN_NAME := $(shell basename $@))
$(eval PLUGIN_PATH := plugins/$(PLUGIN_NAME)/lib$(PLUGIN_NAME).so)
$(eval PLUGIN_VERSION := $(shell ./build/utils/version --path $(PLUGIN_PATH) $(PRE_RELEASE) | tail -n 1))
$(eval PLUGIN_VERSION := $(shell ./build/utils/version --path $(PLUGIN_PATH) --pre-release | tail -n 1))
# re-run command to stop in case of non-zero exit code
@./build/utils/version --path $(PLUGIN_PATH) $(PRE_RELEASE)
@./build/utils/version --path $(PLUGIN_PATH) --pre-release
mkdir -p $(OUTPUT_DIR)/$(PLUGIN_NAME)
cp -r $(PLUGIN_PATH) $(OUTPUT_DIR)/$(PLUGIN_NAME)/
cp -r plugins/$(PLUGIN_NAME)/README.md $(OUTPUT_DIR)/$(PLUGIN_NAME)/
tar -zcvf $(OUTPUT_DIR)/$(PLUGIN_NAME)-$(PLUGIN_VERSION)-${PLATFORM}-${ARCH}.tar.gz -C ${OUTPUT_DIR}/$(PLUGIN_NAME) $$(ls -A ${OUTPUT_DIR}/$(PLUGIN_NAME))
rm -rf $(OUTPUT_DIR)/$(PLUGIN_NAME)
@echo "$(PLUGIN_NAME) package built"
# build rules package, if any
mkdir -p $(OUTPUT_DIR)/$(PLUGIN_NAME)-rules
# symlinks are ignored when creating the rules package. Only regular files are considered.
find plugins/$(PLUGIN_NAME)/rules/* -type f -exec cp -t $(OUTPUT_DIR)/$(PLUGIN_NAME)-rules/ {} + && \
tar -zcvf $(OUTPUT_DIR)/$(PLUGIN_NAME)-rules-$(PLUGIN_VERSION).tar.gz -C \
$(OUTPUT_DIR)/$(PLUGIN_NAME)-rules $$(ls -A ${OUTPUT_DIR}/$(PLUGIN_NAME)-rules) || :
@test $(OUTPUT_DIR)/$(PLUGIN_NAME)-rules-$(PLUGIN_VERSION).tar.gz && echo "$(PLUGIN_NAME) rules package built"
rm -rf $(OUTPUT_DIR)/$(PLUGIN_NAME)-rules
release/%: DEBUG=0
release/%: PRE_RELEASE=
release/%: clean package/%
@echo "$(PLUGIN_NAME) released"
.PHONY: changelogs
changelogs: $(plugins-changelogs)
changelog/%: build/changelog/changelog
release/%: clean/% % build/utils/version
$(eval PLUGIN_NAME := $(shell basename $@))
$(eval CHANGELOG_PATH := plugins/$(PLUGIN_NAME)/CHANGELOG.md)
@./changelog-gen.sh $(PLUGIN_NAME) > $(CHANGELOG_PATH)
@echo "$(CHANGELOG_PATH) generated"
$(eval PLUGIN_PATH := plugins/$(PLUGIN_NAME)/lib$(PLUGIN_NAME).so)
$(eval PLUGIN_VERSION := $(shell ./build/utils/version --path $(PLUGIN_PATH) | tail -n 1))
# re-run command to stop in case of non-zero exit code
@./build/utils/version --path $(PLUGIN_PATH)
mkdir -p $(OUTPUT_DIR)/$(PLUGIN_NAME)
cp -r $(PLUGIN_PATH) $(OUTPUT_DIR)/$(PLUGIN_NAME)/
cp -r plugins/$(PLUGIN_NAME)/README.md $(OUTPUT_DIR)/$(PLUGIN_NAME)/
tar -zcvf $(OUTPUT_DIR)/$(PLUGIN_NAME)-$(PLUGIN_VERSION)-${PLATFORM}-${ARCH}.tar.gz -C ${OUTPUT_DIR}/$(PLUGIN_NAME) $$(ls -A ${OUTPUT_DIR}/$(PLUGIN_NAME))
.PHONY: check-registry
check-registry: build/registry/registry
@build/registry/bin/registry check ./registry.yaml
@build/registry/registry check ./registry.yaml
@echo The plugin registry is OK
.PHONY: update-readme
update-readme: build/registry/registry
@build/registry/bin/registry table ./registry.yaml \
@build/registry/registry table ./registry.yaml \
--subfile=./README.md \
--subtag="<!-- REGISTRY:TABLE -->"
--subtag="<!-- REGISTRY:SOURCE-TABLE -->" \
--type=plugins-source
@build/registry/registry table ./registry.yaml \
--subfile=./README.md \
--subtag="<!-- REGISTRY:EXTRACTOR-TABLE -->" \
--type=plugins-extractor
@echo Readme has been updated successfully
.PHONY: update-index
update-index: build/registry/registry
@build/registry/bin/registry update-index ./registry.yaml ${DIST_INDEX}
@echo Distribution index has been updated successfully
.PHONY: build/utils/version
build/utils/version:
+@cd build/utils && make
@cd build/utils && make
.PHONY: clean/build/utils/version
clean/build/utils/version:
+@cd build/utils && make clean
@cd build/utils && make clean
.PHONY: build/registry/registry
build/registry/registry:
+@cd build/registry && make
@cd build/registry && make
.PHONY: clean/build/registry/registry
clean/build/registry/registry:
+@cd build/registry && make clean
.PHONY: build/changelog/changelog
build/changelog/changelog:
+@cd build/changelog && make
.PHONY: clean/build/changelog/changelog
clean/build/changelog/changelog:
+@cd build/changelog && make clean
.PHONY: build/readme/readme
build/readme/readme:
+@cd build/readme && make
.PHONY: clean/build/readme/readme
clean/build/readme/readme:
+@cd build/readme && make clean
@cd build/registry && make clean

16
OWNERS
View File

@ -1,10 +1,14 @@
approvers:
- mstemm
- leogr
- jasondellaluce
- LucaGuerra
- ekoops
emeritus_approvers:
- ldegio
- leodido
- fntlnz
- mstemm
- leogr
- jasondellaluce
reviewers:
- ldegio
- leodido
- fntlnz
- mstemm
- leogr
- jasondellaluce

150
README.md
View File

@ -1,124 +1,80 @@
# Plugins
[![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 branch checks state](https://img.shields.io/github/checks-status/falcosecurity/plugins/master?style=for-the-badge)
[![License](https://img.shields.io/github/license/falcosecurity/plugins?style=for-the-badge)](./LICENSE)
This repository is the central hub for the Falco Plugin ecosystem. It serves two main purposes:
Note: *The plugin system is a new feature introduced since Falco 0.31.0. You can find more detail in the original [proposal document](https://github.com/falcosecurity/falco/blob/master/proposals/20210501-plugin-system.md).*
- **Be a registry:** A comprehensive catalog of plugins recognized by The Falco Project, regardless of where their source code is hosted.
- **Monorepo for Falcosecurity plugins:** Official plugins hosted and maintained by The Falco Project, with robust release and distribution processes.
For more information about the plugin systems architecture and concepts, please see the [official documentation](https://falco.org/docs/plugins).
---
This repository contains the [Plugin Registry](#plugin-registry) and the [plugins officially maintained](#falcusecurity-plugins) by the Falcosecurity organization. [Plugins](https://falco.org/docs/plugins) can be used to extend [Falco](https://github.com/falcosecurity/falco) and of applications using [Falcosecurity libs](https://github.com/falcosecurity/libs). Please refer to the [official documentation](https://falco.org/docs/plugins) to better understand the plugin system's concepts and architecture.
## Plugin Registry
The registry contains metadata and information about every plugin known and recognized by the Falcosecurity organization. It lists plugins hosted either in this repository or in other repositories. These plugins are developed for Falco and made available to the community.
The Registry contains metadata and information about every plugin known and recognized by the Falcosecurity organization. It lists plugins hosted either in this repository or in other repositories. These plugins are developed for Falco and made available to the community. Check out the sections below to know how to [register your plugins](#registering-a-new-plugin) and see plugins currently contained in the registry.
> Check out the [Registering a Plugin](./docs/registering-a-plugin.md) to know how to add your plugin to this registry.
### Registering a new Plugin
Registering your plugin inside the registry helps ensure that some technical constraints are respected, such as that a [given ID is used by exactly one source plugin](https://falco.org/docs/plugins/#plugin-event-ids) and allows source plugin authors and extractor plugin authors to [coordinate event source formats](https://falco.org/docs/plugins/#plugin-event-sources-and-interoperability). Moreover, this is a great way to share your plugin project with the community and engage with it, thus gaining new users and **increasing its visibility**. We encourage you to register your plugin in this registry before publishing it. You can add your plugins in this registry regardless of where its source code is hosted (there's a `url` field for this specifically).
The registration process involves adding an entry about your plugin inside the [registry.yaml](./registry.yaml) file by creating a Pull Request in this repository. Please be mindful of a few constraints that are automatically checked and required for your plugin to be accepted:
- The `name` field is mandatory and must be **unique** across all the plugins in the registry
- *(Source plugins only)* The `id` field is mandatory and must be **unique** across all the source plugins in the registry
- The plugin `name` and `source` fields should match this [regular expression](https://en.wikipedia.org/wiki/Regular_expression): `^[a-z]+[a-z0-9_]*$`
For reference, here's an example of a source plugin entry:
```yaml
- id: 2
source: aws_cloudtrail
name: cloudtrail
description: ...
authors: The Falco Authors
contact: https://falco.org/community
url: ...
license: Apache-2.0
```
You can find the full registry specification here: *(coming soon...)*
### Registered Plugins
The tables below list all the plugins currently registered. The tables are automatically generated from the [registry.yaml](./registry.yaml) file.
<!-- The text inside \<!-- REGISTRY:xxx --\> comments is auto-generated.
These comments and the text between them should not be edited by hand -->
<!-- REGISTRY:TABLE -->
| Name | Capabilities | Description
| --- | --- | --- |
| plugin-id-zero-value | **Event Sourcing** <br/>ID: 0 <br/>`` | This ID is reserved for particular purposes and cannot be registered. A plugin author should not use this ID unless specified by the documentation. <br/><br/> Authors: N/A <br/> License: N/A |
| test | **Event Sourcing** <br/>ID: 999 <br/>`test` | This ID is reserved for source plugin development. Any plugin author can use this ID, but authors can expect events from other developers with this ID. After development is complete, the author should request an actual ID <br/><br/> Authors: N/A <br/> License: N/A |
| [k8saudit](https://github.com/falcosecurity/plugins/tree/main/plugins/k8saudit) | **Event Sourcing** <br/>ID: 1 <br/>`k8s_audit` <br/>**Field Extraction** <br/> `k8s_audit` | Read Kubernetes Audit Events and monitor Kubernetes Clusters <br/><br/> Authors: [The Falco Authors](https://falco.org/community) <br/> License: Apache-2.0 |
| [cloudtrail](https://github.com/falcosecurity/plugins/tree/main/plugins/cloudtrail) | **Event Sourcing** <br/>ID: 2 <br/>`aws_cloudtrail` <br/>**Field Extraction** <br/> `aws_cloudtrail` | Reads Cloudtrail JSON logs from files/S3 and injects as events <br/><br/> Authors: [The Falco Authors](https://falco.org/community) <br/> License: Apache-2.0 |
| [json](https://github.com/falcosecurity/plugins/tree/main/plugins/json) | **Field Extraction** <br/> *All Sources* | Extract values from any JSON payload <br/><br/> Authors: [The Falco Authors](https://falco.org/community) <br/> License: Apache-2.0 |
| [dummy](https://github.com/falcosecurity/plugins/tree/main/plugins/dummy) | **Event Sourcing** <br/>ID: 3 <br/>`dummy` <br/>**Field Extraction** <br/> `dummy` | Reference plugin used to document interface <br/><br/> Authors: [The Falco Authors](https://falco.org/community) <br/> License: Apache-2.0 |
| [dummy_c](https://github.com/falcosecurity/plugins/tree/main/plugins/dummy_c) | **Event Sourcing** <br/>ID: 4 <br/>`dummy_c` <br/>**Field Extraction** <br/> `dummy_c` | Like dummy, but written in C++ <br/><br/> Authors: [The Falco Authors](https://falco.org/community) <br/> License: Apache-2.0 |
| [docker](https://github.com/Issif/docker-plugin) | **Event Sourcing** <br/>ID: 5 <br/>`docker` <br/>**Field Extraction** <br/> `docker` | Docker Events <br/><br/> Authors: [Thomas Labarussias](https://github.com/Issif) <br/> License: Apache-2.0 |
| [seccompagent](https://github.com/kinvolk/seccompagent) | **Event Sourcing** <br/>ID: 6 <br/>`seccompagent` <br/>**Field Extraction** <br/> `seccompagent` | Seccomp Agent Events <br/><br/> Authors: [Alban Crequy](https://github.com/kinvolk/seccompagent) <br/> License: Apache-2.0 |
| [okta](https://github.com/falcosecurity/plugins/tree/main/plugins/okta) | **Event Sourcing** <br/>ID: 7 <br/>`okta` <br/>**Field Extraction** <br/> `okta` | Okta Log Events <br/><br/> Authors: [The Falco Authors](https://falco.org/community) <br/> License: Apache-2.0 |
| [github](https://github.com/falcosecurity/plugins/tree/main/plugins/github) | **Event Sourcing** <br/>ID: 8 <br/>`github` <br/>**Field Extraction** <br/> `github` | Github Webhook Events <br/><br/> Authors: [The Falco Authors](https://falco.org/community) <br/> License: Apache-2.0 |
| [k8saudit-eks](https://github.com/falcosecurity/plugins/tree/main/plugins/k8saudit-eks) | **Event Sourcing** <br/>ID: 9 <br/>`k8s_audit` <br/>**Field Extraction** <br/> `k8s_audit` | Read Kubernetes Audit Events from AWS EKS Clusters <br/><br/> Authors: [The Falco Authors](https://falco.org/community) <br/> License: Apache-2.0 |
| [nomad](https://github.com/albertollamaso/nomad-plugin/tree/main) | **Event Sourcing** <br/>ID: 10 <br/>`nomad` <br/>**Field Extraction** <br/> `nomad` | Read Hashicorp Nomad Events Stream <br/><br/> Authors: [Alberto Llamas](https://github.com/albertollamaso/nomad-plugin/issues) <br/> License: Apache-2.0 |
| [dnscollector](https://github.com/SysdigDan/dnscollector-falco-plugin) | **Event Sourcing** <br/>ID: 11 <br/>`dnscollector` <br/>**Field Extraction** <br/> `dnscollector` | DNS Collector Events <br/><br/> Authors: [Daniel Moloney](https://github.com/SysdigDan/dnscollector-falco-plugin/issues) <br/> License: Apache-2.0 |
| [gcpaudit](https://github.com/falcosecurity/plugins/tree/main/plugins/gcpaudit) | **Event Sourcing** <br/>ID: 12 <br/>`gcp_auditlog` <br/>**Field Extraction** <br/> `gcp_auditlog` | Read GCP Audit Logs <br/><br/> Authors: [The Falco Authors](https://falco.org/community) <br/> License: Apache-2.0 |
| [syslogsrv](https://github.com/nabokihms/syslogsrv-falco-plugin/tree/main/plugins/syslogsrv) | **Event Sourcing** <br/>ID: 13 <br/>`syslogsrv` <br/>**Field Extraction** <br/> `syslogsrv` | Syslog Server Events <br/><br/> Authors: [Maksim Nabokikh](https://github.com/nabokihms/syslogsrv-falco-plugin/issues) <br/> License: Apache-2.0 |
| [salesforce](https://github.com/an1245/falco-plugin-salesforce/) | **Event Sourcing** <br/>ID: 14 <br/>`salesforce` <br/>**Field Extraction** <br/> `salesforce` | Falco plugin providing basic runtime threat detection and auditing logging for Salesforce <br/><br/> Authors: [Andy](https://github.com/an1245/falco-plugin-salesforce/issues) <br/> License: Apache-2.0 |
| [box](https://github.com/an1245/falco-plugin-box/) | **Event Sourcing** <br/>ID: 15 <br/>`box` <br/>**Field Extraction** <br/> `box` | Falco plugin providing basic runtime threat detection and auditing logging for Box <br/><br/> Authors: [Andy](https://github.com/an1245/falco-plugin-box/issues) <br/> License: Apache-2.0 |
| [k8smeta](https://github.com/falcosecurity/plugins/tree/main/plugins/k8smeta) | **Field Extraction** <br/> `syscall` | Enriche Falco syscall flow with Kubernetes Metadata <br/><br/> Authors: [The Falco Authors](https://falco.org/community) <br/> License: Apache-2.0 |
| [k8saudit-gke](https://github.com/falcosecurity/plugins/tree/main/plugins/k8saudit-gke) | **Event Sourcing** <br/>ID: 16 <br/>`k8s_audit` <br/>**Field Extraction** <br/> `k8s_audit` | Read Kubernetes Audit Events from GKE Clusters <br/><br/> Authors: [The Falco Authors](https://falco.org/community) <br/> License: Apache-2.0 |
| [journald](https://github.com/gnosek/falco-journald-plugin) | **Event Sourcing** <br/>ID: 17 <br/>`journal` <br/>**Field Extraction** <br/> `journal` | Read Journald events into Falco <br/><br/> Authors: [Grzegorz Nosek](https://github.com/gnosek/falco-journald-plugin) <br/> License: Apache-2.0 |
| [kafka](https://github.com/falcosecurity/plugins/tree/main/plugins/kafka) | **Event Sourcing** <br/>ID: 18 <br/>`kafka` | Read events from Kafka topics into Falco <br/><br/> Authors: [Hunter Madison](https://falco.org/community) <br/> License: Apache-2.0 |
| [gitlab](https://github.com/an1245/falco-plugin-gitlab) | **Event Sourcing** <br/>ID: 19 <br/>`gitlab` <br/>**Field Extraction** <br/> `gitlab` | Falco plugin providing basic runtime threat detection and auditing logging for GitLab <br/><br/> Authors: [Andy](https://github.com/an1245/falco-plugin-gitlab/issues) <br/> License: Apache-2.0 |
| [keycloak](https://github.com/mattiaforc/falco-keycloak-plugin) | **Event Sourcing** <br/>ID: 20 <br/>`keycloak` <br/>**Field Extraction** <br/> `keycloak` | Falco plugin for sourcing and extracting Keycloak user/admin events <br/><br/> Authors: [Mattia Forcellese](https://github.com/mattiaforc/falco-keycloak-plugin/issues) <br/> License: Apache-2.0 |
| [k8saudit-aks](https://github.com/falcosecurity/plugins/tree/main/plugins/k8saudit-aks) | **Event Sourcing** <br/>ID: 21 <br/>`k8s_audit` <br/>**Field Extraction** <br/> `k8s_audit` | Read Kubernetes Audit Events from Azure AKS Clusters <br/><br/> Authors: [The Falco Authors](https://falco.org/community) <br/> License: Apache-2.0 |
| [k8saudit-ovh](https://github.com/falcosecurity/plugins/tree/main/plugins/k8saudit-ovh) | **Event Sourcing** <br/>ID: 22 <br/>`k8s_audit` <br/>**Field Extraction** <br/> `k8s_audit` | Read Kubernetes Audit Events from OVHcloud MKS Clusters <br/><br/> Authors: [Aurélie Vache](https://falco.org/community) <br/> License: Apache-2.0 |
| [dummy_rs](https://github.com/falcosecurity/plugins/tree/main/plugins/dummy_rs) | **Event Sourcing** <br/>ID: 23 <br/>`dummy_rs` <br/>**Field Extraction** <br/> `dummy_rs` | Like dummy, but written in Rust <br/><br/> Authors: [The Falco Authors](https://falco.org/community) <br/> License: Apache-2.0 |
| [container](https://github.com/falcosecurity/plugins/tree/main/plugins/container) | **Field Extraction** <br/> `syscall` | Enriche Falco syscall flow with Container Metadata <br/><br/> Authors: [The Falco Authors](https://falco.org/community) <br/> License: Apache-2.0 |
| [krsi](https://github.com/falcosecurity/plugins/tree/main/plugins/krsi) | **Field Extraction** <br/> `syscall` | Security (KRSI) events support for Falco <br/><br/> Authors: [The Falco Authors](https://falco.org/community) <br/> License: Apache-2.0 |
| [collector](https://github.com/falcosecurity/plugins/tree/main/plugins/collector) | **Event Sourcing** <br/>ID: 24 <br/>`collector` | Generic collector to ingest raw payloads into Falco <br/><br/> Authors: [The Falco Authors](https://falco.org/community) <br/> License: Apache-2.0 |
| [awselb](https://github.com/yukinakanaka/falco-plugin-aws-elb) | **Event Sourcing** <br/>ID: 25 <br/>`awselb` <br/>**Field Extraction** <br/> `awselb` | AWS Elastic Load Balancer access logs events <br/><br/> Authors: [Yuki Nakamura](https://github.com/yukinakanaka/falco-plugin-aws-elb/issues) <br/> License: Apache-2.0 |
<!-- The text inside \<!-- REGISTRY:xxx --\> comments is auto-generated. These comments and the text between them should not be edited by hand -->
<!-- REGISTRY:TABLE -->
#### Source Plugins
<!-- REGISTRY:SOURCE-TABLE -->
| ID | Name | Event Source | Description | Info |
| --- | --- | --- | --- | --- |
| 1 | k8s_audit | `k8s_audit` | Reserved for a future back-port of Falco's k8s_audit event source as a plugin | Authors: N/A <br/> License: N/A |
| 2 | [cloudtrail](https://github.com/falcosecurity/plugins/tree/master/plugins/cloudtrail) | `aws_cloudtrail` | Reads Cloudtrail JSON logs from files/S3 and injects as events | Authors: [The Falco Authors](https://falco.org/community) <br/> License: Apache-2.0 |
| 3 | [dummy](https://github.com/falcosecurity/plugins/tree/master/plugins/dummy) | `dummy` | Reference plugin used to document interface | Authors: [The Falco Authors](https://falco.org/community) <br/> License: Apache-2.0 |
| 4 | [dummy_c](https://github.com/falcosecurity/plugins/tree/master/plugins/dummy_c) | `dummy_c` | Like Dummy, but written in C++ | Authors: [The Falco Authors](https://falco.org/community) <br/> License: Apache-2.0 |
| 5 | [docker](https://github.com/Issif/docker-plugin) | `docker` | Docker Events | Authors: [Thomas Labarussias](https://github.org/Issif) <br/> License: Apache-2.0 |
| 6 | [seccompagent](https://github.com/kinvolk/seccompagent) | `seccompagent` | Seccomp Agent Events | Authors: [Alban Crequy](https://github.com/kinvolk/seccompagent) <br/> License: Apache-2.0 |
| 7 | [okta](https://github.com/falcosecurity/plugins/tree/master/plugins/okta) | `okta` | Okta Log Events | Authors: [The Falco Authors](https://falco.org/community) <br/> License: Apache-2.0 |
| 999 | test | `test` | This ID is reserved for source plugin development. Any plugin author can use this ID, but authors can expect events from other developers with this ID. After development is complete, the author should request an actual ID | Authors: N/A <br/> License: N/A |
## Falcosecurity Plugins
<!-- REGISTRY:SOURCE-TABLE -->
Along with the registry, this repository hosts the official plugins maintained by the Falcosecurity organization. Each plugin is an independent project with its own directory in the [plugins folder](https://github.com/falcosecurity/plugins/tree/main/plugins).
#### Extractor Plugins
<!-- REGISTRY:EXTRACTOR-TABLE -->
| Name | Extract Event Sources | Description | Info |
| --- | --- | --- | --- |
| [json](https://github.com/falcosecurity/plugins/tree/master/plugins/json) | N/A | Extract values from any JSON payload | Authors: [The Falco Authors](https://falco.org/community) <br/> License: Apache-2.0 |
The `main` branch reflects the latest development state, and plugins are released on a regular basis. Development builds are published automatically when a Pull Request is merged into `main`, while stable builds are released only when a new tag is created. You can find all published artifacts at [download.falco.org](https://download.falco.org/?prefix=plugins). For details on the release process, please see our [Release Process](./release.md).
<!-- REGISTRY:EXTRACTOR-TABLE -->
The instructions below explain how to install and apply only to plugins from this repository.
## Hosted Plugins
### Installing Plugins
Another purpose of this repository is to host and maintain the plugins owned by the Falcosecurity organization. Each plugin is a standalone project and has its own directory, and they are all inside the [plugins](https://github.com/falcosecurity/plugins/tree/master/plugins) folder.
Plugins hosted in this repository are built and distributed through Falco's official channels. You can easily install them using either [falcoctl](https://github.com/falcosecurity/falcoctl) or the [Falco Helm chart](https://github.com/falcosecurity/charts/tree/master/charts/falco).
The `master` branch contains the most up-to-date state of development, and each plugin is regularly released. Please check our [Release Process](./release.md) to know how plugins are released and how artifacts are distributed. Dev builds are published each time a Pull Request gets merged into `master`, whereas stable builds are released and published only when a new release gets tagged. You can find the published artifacts at https://download.falco.org/?prefix=plugins.
#### Using falcoctl
1. **Install falcoctl:** If you haven't already, follow the [falcoctl installation guide](https://github.com/falcosecurity/falcoctl?tab=readme-ov-file#installation).
2. **Install a Plugin:** Execute the following command, replacing `<plugin-name>` with the name of the plugin you wish to install:
```bash
falcoctl index update falcosecurity
falcoctl artifact install <plugin-name>
```
> Depending on your environment, you may need to run the above commands with `sudo`.
3. Configure Falco to load the plugin as described in the [plugin's documentation](https://falco.org/docs/concepts/plugins/usage/#loading-plugins-in-falco).
#### Using the Falco Helm Chart
When installing Falco using the Helm chart, you can instruct the chart to install a specific plugin by setting the `falcoctl.config.artifact.install.refs` value and then adding the relevant plugin configuration under `falco`.
The Helm charts provides a preset [values-k8saudit.yaml](https://github.com/falcosecurity/charts/blob/master/charts/falco/values-k8saudit.yaml) file that can be used to install the `k8saudit` plugin or as example for installing other plugins.
If you wish to contribute your plugin to the Falcosecurity organization, you just need to open a Pull Request to add it inside the `plugins` folder and to add it inside the registry. In order to be hosted in this repository, plugins 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/main/CONTRIBUTING.md). Code contributions are always encouraged and welcome!
If you wish to contribute a plugin to The Falco Project, simply open a Pull Request to add your plugin to the `/plugins` folder and [update the registry accordingly](./docs/registering-a-plugin.md). Note that to be hosted in this repository, plugins must be licensed under the [Apache 2.0 License](./LICENSE).
### Enforcing coding style and repo policies locally
This repository supports enforcing coding style and policies locally through the `pre-commit` framework. `pre-commit`
allows to automatically install `git-hooks` that will be executed at every new commit. The following is the list of
`git-hooks` defined in `.pre-commit-config.yaml` (notice that some of them only target files written in a specific
language):
1. the `rust-fmt` hook - a `pre-commit` git hook running `rust fmt` on the staged changes
2. the `dco` hook - a `pre-commit-msg` git hook running adding the `DCO` on the commit if not present
The following steps describe how to install these hooks.
##### Step 1
Install `pre-commit` framework following the [official documentation](https://pre-commit.com/#installation).
> __Please note__: you have to follow only the "Installation" section.
#### Step 2
Install `pre-commit` git hooks:
```bash
pre-commit install --hook-type pre-commit --hook-type prepare-commit-msg --overwrite
```
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!
## License

View File

@ -1 +0,0 @@
bin

View File

@ -1,26 +0,0 @@
# 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.
#
SHELL=/bin/bash -o pipefail
GO ?= go
all: bin/changelog
clean:
@rm -fr bin
bin/changelog: changelog.go
@mkdir -p bin
@$(GO) build -o bin/changelog changelog.go

View File

@ -1,172 +0,0 @@
// 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 (
"errors"
"fmt"
"os"
"os/exec"
"regexp"
"strings"
"github.com/falcosecurity/plugins/build/registry/pkg/registry"
"github.com/spf13/pflag"
)
const (
commitHashMaxLen = 7
commitLinkFmt = "https://github.com/falcosecurity/plugins/commit/%s"
commitMsgMaxLen = 80
)
func git(args ...string) (output []string, err error) {
fmt.Fprintln(os.Stderr, "git ", strings.Join(args, " "))
stdout, err := exec.Command("git", args...).Output()
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
return nil, errors.New("git (" + exitErr.String() + "): " + string(exitErr.Stderr))
}
return nil, err
}
return strings.Split(string(stdout), "\n"), nil
}
// an empty string matches the last tag with no match filtering
func gitGetLatestTagWithMatch(match []string) (string, error) {
args := []string{"describe", "--tags", "--abbrev=0"}
if len(match) > 0 {
for _, m := range match {
args = append(args, "--match", m)
}
}
tags, err := git(args...)
if err != nil {
return "", err
}
if len(tags) == 0 {
return "", errors.New("git tag not found")
}
return tags[0], nil
}
// an empty tag lists commit from whole history
func gitListCommits(from, to string) ([]string, error) {
revRange := ""
if len(to) > 0 {
revRange = to
}
if len(from) > 0 {
if len(revRange) == 0 {
revRange = "HEAD"
}
revRange = from + ".." + revRange
}
logs, err := git("log", revRange, "--oneline")
if err != nil {
return nil, err
}
return logs, nil
}
func pluginSource(pname string) string {
reg, err := registry.LoadRegistryFromFile("registry.yaml")
if err != nil {
fail(fmt.Errorf("an error occurred while loading registry entries from file %q: %v", "registry.yaml", err))
}
for _, plugin := range reg.Plugins {
if plugin.Name == pname && plugin.Capabilities.Sourcing.Supported {
return plugin.Capabilities.Sourcing.Source
}
}
return ""
}
func fail(err error) {
fmt.Printf("error: %s\n", err)
os.Exit(1)
}
// formats the line with markdown syntax and decorates it
func formatCommitLine(c string) string {
firstSpace := strings.Index(c, " ")
hash := strings.Trim(c[:firstSpace], " ") // hash is before the first space
message := strings.Trim(c[firstSpace:], " ") // message is after the first space
if len(message) > commitMsgMaxLen {
message = message[:commitMsgMaxLen-3] + "..."
}
commitLink := fmt.Sprintf(commitLinkFmt, hash)
return fmt.Sprintf("* [`%s`](%s) %s", hash[:commitHashMaxLen], commitLink, message)
}
func main() {
var plugin string
var from string
var to string
pflag.StringVar(&plugin, "plugin", "", "Name of the plugin to generate the changelog for")
pflag.StringVar(&from, "from", "", "Tag/branch/hash from which start listing commits")
pflag.StringVar(&to, "to", "HEAD", "Tag/branch/hash to which stop listing commits")
pflag.Parse()
// if from is not specified, we use the latest tag matching the plugin name
if len(from) == 0 {
match := []string{}
if len(plugin) > 0 {
match = append(match, "plugins/"+plugin+"/v[0-9]*.[0-9]*.[0-9]*")
match = append(match, plugin+"-[0-9]*.[0-9]*.[0-9]*")
}
tag, err := gitGetLatestTagWithMatch(match)
if err != nil {
fmt.Fprintln(os.Stderr, "no matching tag found for plugin '"+plugin+"', using commits from whole history:", err.Error())
} else {
from = tag
}
}
// get all commits
commits, err := gitListCommits(from, to)
if err != nil {
fail(err)
}
var rgx, rgxSource, rgxDeps *regexp.Regexp
if len(plugin) > 0 {
// craft a regex to filter all plugin-related commits that follow
// the conventional commit format
rgx, _ = regexp.Compile("^[a-f0-9]+ [a-zA-Z]+\\(([a-zA-Z\\/]+\\/)?" + plugin + "(\\/[a-zA-Z\\/]+)?\\):.*")
// use source name of the plugin as well, if it has sourcing capabilities
pluginSource := pluginSource(plugin)
if pluginSource != "" {
rgxSource, _ = regexp.Compile("^[a-f0-9]+ [a-zA-Z]+\\(([a-zA-Z\\/]+\\/)?" + pluginSource + "(\\/[a-zA-Z\\/]+)?\\):.*")
}
// craft a regex to filter all plugin-related dependabot commits
rgxDeps, _ = regexp.Compile("^[a-f0-9]+ build\\(deps\\):.*" + plugin + "$")
}
for _, c := range commits {
if len(c) > 0 && (rgx == nil || rgx.MatchString(c) ||
(rgxSource != nil && rgxSource.MatchString(c)) ||
rgxDeps.MatchString(c)) {
fmt.Println(formatCommitLine(c) + "\n")
}
}
}

View File

@ -1,39 +0,0 @@
module github.com/falcosecurity/plugins/build/changelog
go 1.23.0
toolchain go1.24.1
require (
github.com/falcosecurity/plugins/build/registry v0.0.0-20240514080945-0e7ef7698747
github.com/spf13/pflag v1.0.6
)
require (
github.com/docker/cli v24.0.5+incompatible // indirect
github.com/docker/docker v25.0.6+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.0 // indirect
github.com/falcosecurity/falcoctl v0.6.1 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc4 // indirect
github.com/pelletier/go-toml/v2 v2.0.9 // indirect
github.com/pkg/errors v0.9.1 // 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/viper v1.16.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
golang.org/x/oauth2 v0.27.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
oras.land/oras-go/v2 v2.2.1 // indirect
)

View File

@ -1,516 +0,0 @@
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=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
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.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/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/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=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
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/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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
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=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
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/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/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 v25.0.6+incompatible h1:5cPwbwriIcsua2REJe8HqQV+6WlWc1byg2QSXzBxBGg=
github.com/docker/docker v25.0.6+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/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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
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/falcosecurity/plugins/build/registry v0.0.0-20240514080945-0e7ef7698747 h1:d+YgxJXgcmu9LX5ixICSTaN3y5MmgCnxW8TfPu5i+Eg=
github.com/falcosecurity/plugins/build/registry v0.0.0-20240514080945-0e7ef7698747/go.mod h1:I/unuAO/urquhDsyOE+YmcY0FNBInVtLfZ5VwD3FUMo=
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/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/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=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
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/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=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
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/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=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
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.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
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=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
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-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/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/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
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/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
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/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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
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/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
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/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.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0=
github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
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.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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
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/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
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.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.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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
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/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=
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=
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-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-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
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=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
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/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=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
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/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=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
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/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-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=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
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-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-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=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
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-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-20201209123823-ac852fbbde11/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-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
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=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
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.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
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=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-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-20191001151750-bb3f8db39f24/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-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=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20200523222454-059865788121/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=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
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.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.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
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/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-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-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-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=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
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-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-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=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
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-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
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=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
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/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=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
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-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=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
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-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-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=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
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.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/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=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
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.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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
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=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
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=
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=

View File

@ -1,2 +0,0 @@
bin
readme

View File

@ -1,26 +0,0 @@
# 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.
#
SHELL=/bin/bash -o pipefail
GO ?= go
all: bin/readme
clean:
@rm -fr bin
bin/readme: readme.go fields.go
@mkdir -p bin
@$(GO) build -o bin/readme readme.go fields.go

View File

@ -1,87 +0,0 @@
// 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 (
"bytes"
"strings"
"github.com/falcosecurity/plugin-sdk-go/pkg/loader"
"github.com/falcosecurity/plugin-sdk-go/pkg/sdk"
"github.com/olekukonko/tablewriter"
)
const (
defaultFieldsTag = "README-PLUGIN-FIELDS"
)
func fieldsRenderArgRow(a *sdk.FieldEntryArg) string {
if !a.IsIndex && !a.IsKey {
return "None"
}
var res []string
if a.IsIndex {
res = append(res, "Index")
}
if a.IsKey {
res = append(res, "Key")
}
if a.IsRequired {
res = append(res, "Required")
}
return strings.Join(res, ", ")
}
// renderNewLines replaces '\n' character with "<br/>" for proper table formatting.
func renderNewLines(desc string) string {
return strings.ReplaceAll(desc, "\n", "<br/>")
}
func fieldsEditor(p *loader.Plugin, s string) (string, error) {
if !p.HasCapExtraction() {
return s, nil
}
fields := p.Fields()
if len(fields) == 0 {
return s, nil
}
var buf bytes.Buffer
table := tablewriter.NewWriter(&buf)
table.SetHeader([]string{"Name", "Type", "Arg", "Description"})
table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
table.SetCenterSeparator("|")
table.SetRowSeparator("-")
table.SetAutoWrapText(false)
for _, f := range fields {
row := []string{}
row = append(row, "`"+f.Name+"`")
if f.IsList {
row = append(row, "`"+f.Type+" (list)`")
} else {
row = append(row, "`"+f.Type+"`")
}
row = append(row, fieldsRenderArgRow(&f.Arg))
row = append(row, renderNewLines(f.Desc))
table.Append(row)
}
table.Render()
return replateTag(s, fieldsTag, buf.String())
}

View File

@ -1,9 +0,0 @@
module github.com/falcosecurity/plugins/build/readme
go 1.13
require (
github.com/falcosecurity/plugin-sdk-go v0.7.5
github.com/olekukonko/tablewriter v0.0.5
github.com/spf13/pflag v1.0.6
)

View File

@ -1,33 +0,0 @@
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/falcosecurity/plugin-sdk-go v0.7.5 h1:ke/+kTt0PwedM8+IGTKcW3LrUI/xiJNDCSzqTSW+CvI=
github.com/falcosecurity/plugin-sdk-go v0.7.5/go.mod h1:NP+y22DYOS+G3GDXIXNmzf0CBL3nfPPMoQuHvAzfitQ=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
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/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
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.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
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=

View File

@ -1,102 +0,0 @@
// 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 (
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/falcosecurity/plugin-sdk-go/pkg/loader"
"github.com/spf13/pflag"
)
var (
pluginPath string
readmePath string
fieldsTag string
)
type EditorFunc func(*loader.Plugin, string) (string, error)
func fail(err error) {
println(err.Error())
os.Exit(1)
}
func replateTag(s string, t string, r string) (string, error) {
startTag := "<!-- " + t + " -->\n"
endTag := "<!-- /" + t + " -->\n"
start := 0
for {
start = strings.Index(s[start:], startTag)
if start < 0 {
return s, nil
}
start += len(startTag)
end := strings.Index(s[start:], endTag)
if end < 0 {
return "", fmt.Errorf("can't find end tag: " + endTag)
}
end += start
s = s[:start] + r + s[end:]
start += len(r) + len(endTag)
}
}
func editFile(plugin *loader.Plugin, path string, editors ...EditorFunc) error {
bytes, err := ioutil.ReadFile(path)
if err != nil {
return err
}
edited := string(bytes)
for _, editor := range editors {
edited, err = editor(plugin, edited)
if err != nil {
return err
}
}
return ioutil.WriteFile(path, ([]byte)(edited), 0)
}
func main() {
pflag.StringVarP(&pluginPath, "plugin", "p", "", "File path to the plugin shared library.")
pflag.StringVarP(&readmePath, "file", "f", "", "File path to the README file to be edited.")
pflag.StringVar(&fieldsTag, "fields-tag", defaultFieldsTag, "Tag to substitute with the plugin fields table.\nIn the file, formatted as \"<!-- TAG -->\\n...\\n<!-- /TAG -->\".")
pflag.Parse()
if len(pluginPath) == 0 {
fail(fmt.Errorf("must specify a plugin path with the -p option"))
}
if len(readmePath) == 0 {
fail(fmt.Errorf("must specify a file path with the -f option"))
}
// load plugin
plugin, err := loader.NewPlugin(pluginPath)
if err != nil {
fail(err)
}
defer plugin.Unload()
// use plugin info to edit readme file
err = editFile(plugin, readmePath, fieldsEditor)
if err != nil {
fail(err)
}
}

View File

@ -1 +1 @@
bin/*
registry

View File

@ -1,6 +1,5 @@
# SPDX-License-Identifier: Apache-2.0
#
# Copyright (C) 2023 The Falco Authors.
# Copyright (C) 2022 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
@ -19,8 +18,7 @@ GO ?= go
all: registry
clean:
@rm -fr bin
@rm -f registry
registry:
@mkdir -p bin
@$(GO) build -o bin/registry ./cmd/registry/...
registry: *.go
@$(GO) build -o registry *.go

102
build/registry/check.go Normal file
View File

@ -0,0 +1,102 @@
/*
Copyright (C) 2022 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 (
"fmt"
"os"
"regexp"
)
var (
rgxName *regexp.Regexp
)
func init() {
var err error
rgxName, err = regexp.Compile(`^[a-z]+[a-z0-9_]*$`)
if err != nil {
println(err.Error())
os.Exit(1)
}
}
func (s *Source) Check(reserved []string) error {
if s.ID == 0 {
return fmt.Errorf("forbidden source ID: '%d'", s.ID)
}
if !rgxName.MatchString(s.Name) {
return fmt.Errorf("name does follow the naming convention: '%s'", s.Name)
}
for _, source := range reserved {
if s.Source == source {
return fmt.Errorf("forbidden source name: '%s'", s.Source)
}
}
if !rgxName.MatchString(s.Source) {
return fmt.Errorf("source name does follow the naming convention: '%s'", s.Source)
}
return nil
}
func (e *Extractor) Check() error {
if !rgxName.MatchString(e.Name) {
return fmt.Errorf("name does follow the naming convention: '%s'", e.Name)
}
return nil
}
func (p *Plugins) Check(reserved []string) error {
ids := make(map[uint]bool)
names := make(map[string]bool)
for _, s := range p.Source {
if err := s.Check(reserved); err != nil {
return err
}
if _, ok := names[s.Name]; ok {
return fmt.Errorf("plugin name is not unique: '%s'", s.Name)
}
if _, ok := ids[s.ID]; ok {
return fmt.Errorf("source id is not unique: '%d'", s.ID)
}
names[s.Name] = true
ids[s.ID] = true
}
for _, e := range p.Extractor {
if err := e.Check(); err != nil {
return err
}
if _, ok := names[e.Name]; ok {
return fmt.Errorf("plugin name is not unique: '%s'", e.Name)
}
names[e.Name] = true
}
return nil
}
func (r *Registry) Check() error {
return r.Plugins.Check(r.ReservedSources)
}

View File

@ -1,128 +0,0 @@
// 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 (
"bufio"
"context"
"fmt"
"os"
"github.com/falcosecurity/plugins/build/registry/cmd/validateRegistry"
"github.com/spf13/cobra"
"github.com/falcosecurity/plugins/build/registry/internal/options"
"github.com/falcosecurity/plugins/build/registry/pkg/check"
"github.com/falcosecurity/plugins/build/registry/pkg/distribution"
"github.com/falcosecurity/plugins/build/registry/pkg/oci"
"github.com/falcosecurity/plugins/build/registry/pkg/table"
)
const (
defaultTableSubTag = "<!-- REGISTRY -->"
)
var (
out = bufio.NewWriter(os.Stdout)
)
func main() {
defer out.Flush()
opts := options.NewCommonOptions(
options.WithContext(context.Background()),
options.WithOutput(out),
)
checkCmd := &cobra.Command{
Use: "check <filename>",
Short: "Verify the correctness of a plugin registry YAML file",
Args: cobra.ExactArgs(1),
DisableFlagsInUseLine: true,
RunE: func(c *cobra.Command, args []string) error {
return check.DoCheck(args[0])
},
}
var tableSubFileName string
var tableSubTab string
tableCmd := &cobra.Command{
Use: "table <filename>",
Short: "Format a plugin registry YAML file in a MarkDown table",
Args: cobra.ExactArgs(1),
RunE: func(c *cobra.Command, args []string) error {
return table.DoTable(args[0], tableSubFileName, tableSubTab)
},
}
tableFlags := tableCmd.Flags()
tableFlags.StringVar(&tableSubTab, "subtag", defaultTableSubTag, "A tag that delimits the start and the end of the text section to substitute with the generated table.")
tableFlags.StringVar(&tableSubFileName, "subfile", "", "If specified, the table will be written inside the file at this path, inserting it between the first two instances of the substitution tag.")
updateIndexCmd := &cobra.Command{
Use: "update-index <registryFilename> <indexFilename>",
Short: "Update an index file for artifacts distribution using registry data",
Args: cobra.ExactArgs(2),
DisableFlagsInUseLine: true,
RunE: func(c *cobra.Command, args []string) error {
return distribution.DoUpdateIndex(args[0], args[1])
},
}
var (
pluginsAMD64Path string
pluginsARM64Path string
rulesfilesPath string
devTag string
)
updateOCIRegistry := &cobra.Command{
Use: "update-oci-registry <registryFilename>",
Short: "Update the oci registry starting from the registry file and s3 bucket",
Args: cobra.ExactArgs(1),
DisableFlagsInUseLine: true,
RunE: func(c *cobra.Command, args []string) error {
status, err := oci.DoUpdateOCIRegistry(opts.Context, args[0], pluginsAMD64Path, pluginsARM64Path, rulesfilesPath, devTag)
if err != nil {
return err
}
return oci.PrintUpdateStatus(status, opts.Output)
},
}
ociFlags := updateOCIRegistry.Flags()
ociFlags.StringVar(&pluginsAMD64Path, "plugins-amd64-path", "", "Path to plugins for the amd64 architecture")
ociFlags.StringVar(&pluginsARM64Path, "plugins-arm64-path", "", "Path to plugins for the arm64 architecture")
ociFlags.StringVar(&rulesfilesPath, "rulesfiles-path", "", "Path to rulesfiles")
ociFlags.StringVar(&devTag, "dev-tag", "", "Tag for devel versions")
rootCmd := &cobra.Command{
Use: "registry",
Version: "0.2.0",
}
rootCmd.AddCommand(checkCmd)
rootCmd.AddCommand(tableCmd)
rootCmd.AddCommand(updateIndexCmd)
rootCmd.AddCommand(updateOCIRegistry)
rootCmd.AddCommand(validateRegistry.NewValidateRegistry(context.Background()))
if err := rootCmd.Execute(); err != nil {
fmt.Printf("error: %s\n", err)
os.Exit(1)
}
}

View File

@ -1,57 +0,0 @@
package validateRegistry
import (
"context"
"fmt"
"strings"
"github.com/falcosecurity/falcoctl/pkg/oci/authn"
ocipuller "github.com/falcosecurity/falcoctl/pkg/oci/puller"
"github.com/falcosecurity/plugins/build/registry/pkg/oci"
"github.com/falcosecurity/plugins/build/registry/pkg/registry"
"github.com/spf13/cobra"
"k8s.io/klog/v2"
)
func NewValidateRegistry(ctx context.Context) *cobra.Command {
updateOCIRegistry := &cobra.Command{
Use: "validate-registry <registryFilename>",
Short: "Check that an OCI repo exists for each plugin in the registry file",
Args: cobra.ExactArgs(1),
DisableFlagsInUseLine: true,
RunE: func(c *cobra.Command, args []string) error {
return validateRegistry(ctx, args[0])
},
}
return updateOCIRegistry
}
func validateRegistry(ctx context.Context, registryFile string) error {
reg, err := registry.LoadRegistryFromFile(registryFile)
if err != nil {
return fmt.Errorf("an error occurred while loading registry entries from file %q: %v", registryFile, err)
}
ociClient := authn.NewClient()
puller := ocipuller.NewPuller(ociClient, false, nil)
// For each plugin in the registry index, look for new ones to be released, and publish them.
for _, plugin := range reg.Plugins {
// Filter out plugins that are not owned by falcosecurity.
if !strings.HasPrefix(plugin.URL, oci.PluginsRepo) {
klog.V(2).Infof("skipping plugin %q with authors %q: it is not maintained by %q",
plugin.Name, plugin.Authors, oci.FalcoAuthors)
continue
}
klog.Infof("Checking OCI repo for plugin %q", plugin.Name)
ref := fmt.Sprintf("ghcr.io/falcosecurity/plugins/plugin/%s:latest", plugin.Name)
// We just retrieve the descriptor from the remote repository,
// if it fails, likely the repository does not exist
if _, err := puller.Descriptor(ctx, ref); err != nil {
return fmt.Errorf("plugin %s seems to not have an OCI repository: %w", plugin.Name, err)
}
}
return nil
}

View File

@ -1,165 +1,8 @@
module github.com/falcosecurity/plugins/build/registry
go 1.23.4
toolchain go1.24.1
go 1.13
require (
github.com/blang/semver v3.5.1+incompatible
github.com/falcosecurity/falcoctl v0.11.0
github.com/falcosecurity/plugin-sdk-go v0.7.5
github.com/onsi/ginkgo/v2 v2.23.4
github.com/onsi/gomega v1.37.0
github.com/pkg/errors v0.9.1
github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.10.0
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/klog/v2 v2.130.1
oras.land/oras-go/v2 v2.6.0
)
require (
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/metadata v0.6.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/chai2010/gettext-go v1.0.3 // indirect
github.com/cilium/ebpf v0.17.3 // indirect
github.com/containerd/console v1.0.4 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/creasty/defaults v1.8.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/cli v27.5.1+incompatible // indirect
github.com/docker/docker v27.5.1+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.2 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emicklei/go-restful/v3 v3.12.1 // indirect
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
github.com/falcosecurity/driverkit v0.20.5 // indirect
github.com/fatih/camelcase v1.0.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.25.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/hashicorp/hcl v1.0.1-vault-7 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/lithammer/fuzzysearch v1.1.8 // indirect
github.com/magiconair/properties v1.8.9 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/user v0.3.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/moby/term v0.5.2 // 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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/pterm/pterm v0.12.80 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.12.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/spf13/viper v1.19.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/exp v0.0.0-20250215185904-eff6e970281f // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/oauth2 v0.27.0 // indirect
golang.org/x/sync v0.14.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/time v0.10.0 // indirect
golang.org/x/tools v0.31.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
k8s.io/api v0.32.3 // indirect
k8s.io/apimachinery v0.32.3 // indirect
k8s.io/cli-runtime v0.32.2 // indirect
k8s.io/client-go v0.32.2 // indirect
k8s.io/component-base v0.32.2 // indirect
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect
k8s.io/kubectl v0.32.2 // indirect
k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
modernc.org/libc v1.61.13 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.8.2 // indirect
modernc.org/sqlite v1.35.0 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/kustomize/api v0.19.0 // indirect
sigs.k8s.io/kustomize/kyaml v0.19.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
github.com/go-yaml/yaml v2.1.0+incompatible
github.com/spf13/cobra v1.3.0
)

File diff suppressed because it is too large Load Diff

View File

@ -1,52 +0,0 @@
// 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 options
import (
"context"
"io"
)
type CommonOptions struct {
Output io.Writer
Context context.Context
}
type CommonOption func(opts *CommonOptions)
func NewCommonOptions(opts ...CommonOption) *CommonOptions {
o := &CommonOptions{}
for _, f := range opts {
f(o)
}
return o
}
func WithOutput(out io.Writer) CommonOption {
return func(opts *CommonOptions) {
opts.Output = out
}
}
func WithContext(ctx context.Context) CommonOption {
return func(opts *CommonOptions) {
opts.Context = ctx
}
}

View File

@ -1,12 +0,0 @@
package check
import "github.com/falcosecurity/plugins/build/registry/pkg/registry"
// DoCheck loads the registry.yaml file from disk and validates it.
func DoCheck(fileName string) error {
registry, err := registry.LoadRegistryFromFile(fileName)
if err != nil {
return err
}
return registry.Validate()
}

View File

@ -1,28 +0,0 @@
// 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 common
const (
RulesArtifactSuffix = "-rules"
// EngineVersionKey is the name given to all the engine requirements.
// The same name used by Falco when outputting the engine version.
EngineVersionKey = "engine_version_semver"
// PluginAPIVersion is the name givet to the plugin api version requirements.
// The same name used by Falco when outputting the plugin api version
PluginAPIVersion = "plugin_api_version"
)

View File

@ -1,99 +0,0 @@
// 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 common
import (
"archive/tar"
"compress/gzip"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
)
// ExtractTarGz extracts a *.tar.gz compressed archive and moves its content to destDir.
// Returns a slice containing the full path of the extracted files.
func ExtractTarGz(fileName, destDir string) ([]string, error) {
var files []string
gzipStream, err := os.Open(fileName)
if err != nil {
return nil, fmt.Errorf("unable to open file %q: %w", fileName, err)
}
uncompressedStream, err := gzip.NewReader(gzipStream)
if err != nil {
return nil, err
}
tarReader := tar.NewReader(uncompressedStream)
for {
header, err := tarReader.Next()
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return nil, err
}
switch header.Typeflag {
case tar.TypeDir:
return nil, fmt.Errorf("unexepected dir inside the archive, expected to find only files without any tree structure")
case tar.TypeReg, tar.TypeSymlink:
f := filepath.Join(destDir, filepath.Clean(header.Name))
if !strings.HasPrefix(f, filepath.Clean(destDir)+string(os.PathSeparator)) {
return nil, fmt.Errorf("illegal file path: %q", f)
}
outFile, err := os.Create(filepath.Clean(f))
if err != nil {
return nil, err
}
if err = copyInChunks(outFile, tarReader); err != nil {
return nil, err
}
if err = outFile.Close(); err != nil {
return nil, err
}
files = append(files, f)
default:
return nil, fmt.Errorf("extractTarGz: uknown type: %b in %s", header.Typeflag, header.Name)
}
}
return files, nil
}
func copyInChunks(dst io.Writer, src io.Reader) error {
for {
_, err := io.CopyN(dst, src, 1024)
if err != nil {
if errors.Is(err, io.EOF) {
break
}
return err
}
}
return nil
}

View File

@ -1,30 +0,0 @@
// 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 distribution_test
import (
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestDistribution(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Distribution Suite")
}

View File

@ -1,190 +0,0 @@
// 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 distribution
import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/falcosecurity/plugins/build/registry/pkg/common"
"github.com/falcosecurity/falcoctl/pkg/index/index"
falcoctloci "github.com/falcosecurity/falcoctl/pkg/oci"
"github.com/falcosecurity/falcoctl/pkg/oci/authn"
"github.com/falcosecurity/plugins/build/registry/pkg/oci"
"github.com/falcosecurity/plugins/build/registry/pkg/registry"
"oras.land/oras-go/v2/errdef"
"oras.land/oras-go/v2/registry/remote"
"oras.land/oras-go/v2/registry/remote/auth"
)
// Define our conventions.
const (
GHOrg = "falcosecurity"
)
func PluginToIndexEntry(p registry.Plugin, registry, repo string) *index.Entry {
return &index.Entry{
Name: p.Name,
Type: string(falcoctloci.Plugin),
Registry: registry,
Signature: p.Signature,
Repository: repo,
Description: p.Description,
Home: p.URL,
Keywords: appendIfNotPresent(p.Keywords, p.Name),
License: p.License,
Maintainers: p.Maintainers,
Sources: []string{p.URL},
}
}
func PluginRulesToIndexEntry(p registry.Plugin, registry, repo string) *index.Entry {
return &index.Entry{
Name: p.Name + common.RulesArtifactSuffix,
Type: string(falcoctloci.Rulesfile),
Registry: registry,
Signature: p.Signature,
Repository: repo,
Description: p.Description,
Home: p.URL,
Keywords: appendIfNotPresent(p.Keywords, p.Name+common.RulesArtifactSuffix),
License: p.License,
Maintainers: p.Maintainers,
Sources: []string{p.RulesURL},
}
}
func upsertIndex(r *registry.Registry, ociArtifacts map[string]string, indexPath string) error {
i := index.New(GHOrg)
if err := i.Read(indexPath); err != nil {
return err
}
for _, p := range r.Plugins {
// If the plugins is reserved than we just skip it.
if p.Reserved {
continue
}
// We only publish falcosecurity artifacts that have been uploaded to the repo.
if refPlugin, ok := ociArtifacts[p.Name]; ok {
tokens := strings.Split(refPlugin, "/")
ociRegistry := tokens[0]
ociRepo := filepath.Join(tokens[1:]...)
i.Upsert(PluginToIndexEntry(p, ociRegistry, ociRepo))
}
if refRulesfile, ok := ociArtifacts[p.Name+common.RulesArtifactSuffix]; ok {
tokens := strings.Split(refRulesfile, "/")
ociRegistry := tokens[0]
ociRepo := filepath.Join(tokens[1:]...)
i.Upsert(PluginRulesToIndexEntry(p, ociRegistry, ociRepo))
}
}
return i.Write(indexPath)
}
func DoUpdateIndex(registryFile, indexFile string) error {
var user, reg string
var found bool
if user, found = os.LookupEnv(oci.RegistryUser); !found {
return fmt.Errorf("environment variable with key %q not found, please set it before running this tool", oci.RegistryUser)
}
if reg, found = os.LookupEnv(oci.RegistryOCI); !found {
return fmt.Errorf("environment variable with key %q not found, please set it before running this tool", oci.RegistryOCI)
}
registryEntries, err := registry.LoadRegistryFromFile(registryFile)
if err != nil {
return err
}
ociEntries, err := ociRepos(registryEntries, reg, user)
if err != nil {
return err
}
if err := registryEntries.Validate(); err != nil {
return err
}
return upsertIndex(registryEntries, ociEntries, indexFile)
}
func ociRepos(registryEntries *registry.Registry, reg, user string) (map[string]string, error) {
ociClient := authn.NewClient(authn.WithCredentials(&auth.EmptyCredential))
ociEntries := make(map[string]string)
for _, entry := range registryEntries.Plugins {
if err := ociRepo(ociEntries, ociClient, oci.PluginNamespace, reg, user, entry.Name); err != nil {
return nil, err
}
if entry.RulesURL != "" {
if err := ociRepo(ociEntries, ociClient, oci.RulesfileNamespace, reg, user, entry.Name); err != nil {
return nil, err
}
}
}
return ociEntries, nil
}
func ociRepo(ociEntries map[string]string, client remote.Client, ociRepoNamespace, reg, user, artifactName string) error {
ref := filepath.Join(reg, user, ociRepoNamespace, artifactName)
if ociRepoNamespace == oci.RulesfileNamespace {
artifactName = artifactName + common.RulesArtifactSuffix
}
repo, err := remote.NewRepository(ref)
if err != nil {
return fmt.Errorf("unable to create repo for ref %q: %w", ref, err)
}
repo.Client = client
_, _, err = repo.FetchReference(context.Background(), ref+":latest")
if err != nil && (errors.Is(err, errdef.ErrNotFound) || strings.Contains(err.Error(), "requested access to the resource is denied")) {
return nil
}
if err != nil {
return fmt.Errorf("unable to fetch reference for %q: %w", ref+":latest", err)
}
ociEntries[artifactName] = ref
return nil
}
// 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)
}

View File

@ -1,62 +0,0 @@
// 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 distribution_test
import (
"reflect"
"testing"
"github.com/falcosecurity/falcoctl/pkg/index/index"
"github.com/falcosecurity/plugins/build/registry/pkg/distribution"
"github.com/falcosecurity/plugins/build/registry/pkg/registry"
)
func TestPluginToIndexEntrySignature(t *testing.T) {
t.Parallel()
signature := &index.Signature{
Cosign: &index.CosignSignature{},
}
expected := signature
p := registry.Plugin{Signature: signature}
entry := distribution.PluginToIndexEntry(p, "", "")
if !reflect.DeepEqual(entry.Signature, expected) {
t.Fatalf("Index entry signature: expected %#v, got %v", expected, entry.Signature)
}
}
func TestPluginRulesToIndexEntrySignature(t *testing.T) {
t.Parallel()
signature := &index.Signature{
Cosign: &index.CosignSignature{},
}
expected := signature
p := registry.Plugin{Signature: signature}
entry := distribution.PluginRulesToIndexEntry(p, "", "")
if !reflect.DeepEqual(entry.Signature, expected) {
t.Fatalf("Index entry signature: expected %#v, got %v", expected, entry.Signature)
}
}

View File

@ -1,86 +0,0 @@
// 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 distribution_test
import (
"os"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/falcosecurity/plugins/build/registry/pkg/distribution"
)
const (
indexFile = "testdata/index.yaml"
wrongIndexFile = "testdata/wrong-index.yaml"
registryFile = "testdata/registry.yaml"
wrongRegistryFile = "testdata/wrong-registry.yaml"
registryUser = "falcosecurity"
registryName = "ghcr.io"
)
var _ = Describe("Update index", func() {
var (
err error
)
Context("with registry file", func() {
BeforeEach(func() {
os.Setenv("REGISTRY_USER", registryUser)
os.Setenv("REGISTRY", registryName)
})
Context("with index file", func() {
BeforeEach(func() {
err = distribution.DoUpdateIndex(registryFile, indexFile)
})
It("Should not fail", func() {
Expect(err).To(BeNil())
})
})
Context("without index file", func() {
BeforeEach(func() {
err = distribution.DoUpdateIndex(registryFile, wrongIndexFile)
})
It("Should fail", func() {
Expect(err).ToNot(BeNil())
})
})
})
Context("without registry file", func() {
BeforeEach(func() {
os.Setenv("REGISTRY_USER", registryUser)
os.Setenv("REGISTRY", registryName)
})
Context("with index file", func() {
BeforeEach(func() {
err = distribution.DoUpdateIndex(wrongRegistryFile, indexFile)
})
It("Should fail", func() {
Expect(err).ToNot(BeNil())
})
})
Context("without index file", func() {
BeforeEach(func() {
err = distribution.DoUpdateIndex(wrongRegistryFile, wrongIndexFile)
})
It("Should fail", func() {
Expect(err).ToNot(BeNil())
})
})
})
})

View File

@ -1,247 +0,0 @@
- name: application-rules
type: rulesfile
registry: ghcr.io
repository: falcosecurity/rules/application-rules
description: Application rules
home: https://github.com/falcosecurity/rules/blob/main/rules/application_rules.yaml
keywords:
- application-rules
license: apache-2.0
maintainers:
- email: cncf-falco-dev@lists.cncf.io
name: The Falco Authors
sources:
- https://github.com/falcosecurity/rules/blob/main/rules/application_rules.yaml
- name: cloudtrail
type: plugin
registry: ghcr.io
repository: falcosecurity/plugins/plugin/cloudtrail
description: Reads Cloudtrail JSON logs from files/S3 and injects as events
home: https://github.com/falcosecurity/plugins/tree/main/plugins/cloudtrail
keywords:
- audit
- user-activity
- api-usage
- aws
- cloudtrail
license: Apache-2.0
maintainers:
- email: cncf-falco-dev@lists.cncf.io
name: The Falco Authors
sources:
- https://github.com/falcosecurity/plugins/tree/main/plugins/cloudtrail
- name: cloudtrail-rules
type: rulesfile
registry: ghcr.io
repository: falcosecurity/plugins/ruleset/cloudtrail
description: Reads Cloudtrail JSON logs from files/S3 and injects as events
home: https://github.com/falcosecurity/plugins/tree/main/plugins/cloudtrail
keywords:
- audit
- user-activity
- api-usage
- aws
- cloudtrail-rules
license: Apache-2.0
maintainers:
- email: cncf-falco-dev@lists.cncf.io
name: The Falco Authors
sources:
- https://github.com/falcosecurity/plugins/tree/main/plugins/cloudtrail/rules
- name: dummy
type: plugin
registry: ghcr.io
repository: falcosecurity/plugins/plugin/dummy
description: Reference plugin used to document interface
home: https://github.com/falcosecurity/plugins/tree/main/plugins/dummy
keywords:
- dummy
license: Apache-2.0
maintainers:
- email: cncf-falco-dev@lists.cncf.io
name: The Falco Authors
sources:
- https://github.com/falcosecurity/plugins/tree/main/plugins/dummy
- name: dummy_c
type: plugin
registry: ghcr.io
repository: falcosecurity/plugins/plugin/dummy_c
description: Like dummy, but written in C++
home: https://github.com/falcosecurity/plugins/tree/main/plugins/dummy_c
keywords:
- dummy_c
license: Apache-2.0
maintainers:
- email: cncf-falco-dev@lists.cncf.io
name: The Falco Authors
sources:
- https://github.com/falcosecurity/plugins/tree/main/plugins/dummy_c
- name: falco-rules
type: rulesfile
registry: ghcr.io
repository: falcosecurity/rules/falco-rules
description: Falco rules that are loaded by default
home: https://github.com/falcosecurity/rules/blob/main/rules/falco_rules.yaml
keywords:
- falco-rules
license: apache-2.0
maintainers:
- email: cncf-falco-dev@lists.cncf.io
name: The Falco Authors
sources:
- https://github.com/falcosecurity/rules/blob/main/rules/falco_rules.yaml
- name: github
type: plugin
registry: ghcr.io
repository: falcosecurity/plugins/plugin/github
description: Github Webhook Events
home: https://github.com/falcosecurity/plugins/tree/main/plugins/github
keywords:
- audit
- log-events
- webhook
- github-activity
- github
license: Apache-2.0
maintainers:
- email: cncf-falco-dev@lists.cncf.io
name: The Falco Authors
sources:
- https://github.com/falcosecurity/plugins/tree/main/plugins/github
- name: github-rules
type: rulesfile
registry: ghcr.io
repository: falcosecurity/plugins/ruleset/github
description: Github Webhook Events
home: https://github.com/falcosecurity/plugins/tree/main/plugins/github
keywords:
- audit
- log-events
- webhook
- github-activity
- github
- github-rules
license: Apache-2.0
maintainers:
- email: cncf-falco-dev@lists.cncf.io
name: The Falco Authors
sources:
- https://github.com/falcosecurity/plugins/tree/main/plugins/github/rules
- name: json
type: plugin
registry: ghcr.io
repository: falcosecurity/plugins/plugin/json
description: Extract values from any JSON payload
home: https://github.com/falcosecurity/plugins/tree/main/plugins/json
keywords:
- json-events
- json-payload
- extractor
- json
license: Apache-2.0
maintainers:
- email: cncf-falco-dev@lists.cncf.io
name: The Falco Authors
sources:
- https://github.com/falcosecurity/plugins/tree/main/plugins/json
- name: k8saudit
type: plugin
registry: ghcr.io
repository: falcosecurity/plugins/plugin/k8saudit
description: Read Kubernetes Audit Events and monitor Kubernetes Clusters
home: https://github.com/falcosecurity/plugins/tree/main/plugins/k8saudit
keywords:
- audit
- audit-log
- audit-events
- kubernetes
- k8saudit
license: Apache-2.0
maintainers:
- email: cncf-falco-dev@lists.cncf.io
name: The Falco Authors
sources:
- https://github.com/falcosecurity/plugins/tree/main/plugins/k8saudit
- name: k8saudit-eks
type: plugin
registry: ghcr.io
repository: falcosecurity/plugins/plugin/k8saudit-eks
description: Read Kubernetes Audit Events from AWS EKS Clusters
home: https://github.com/falcosecurity/plugins/tree/main/plugins/k8saudit-eks
keywords:
- audit
- audit-log
- audit-events
- kubernetes
- eks
- aws
- k8saudit-eks
license: Apache-2.0
maintainers: []
sources:
- https://github.com/falcosecurity/plugins/tree/main/plugins/k8saudit-eks
- name: k8saudit-rules
type: rulesfile
registry: ghcr.io
repository: falcosecurity/plugins/ruleset/k8saudit
description: Read Kubernetes Audit Events and monitor Kubernetes Clusters
home: https://github.com/falcosecurity/plugins/tree/main/plugins/k8saudit
keywords:
- audit
- audit-log
- audit-events
- kubernetes
- k8saudit-rules
license: Apache-2.0
maintainers:
- email: cncf-falco-dev@lists.cncf.io
name: The Falco Authors
sources:
- https://github.com/falcosecurity/plugins/tree/main/plugins/k8saudit/rules
- name: okta
type: plugin
registry: ghcr.io
repository: falcosecurity/plugins/plugin/okta
signature:
cosign:
certificate-oidc-issuer: https://token.actions.githubusercontent.com
certificate-oidc-issuer-regexp: ""
certificate-identity: ""
certificate-identity-regexp: https://github.com/LucaGuerra/cool-falco-ruleset/
certificate-github-workflow: ""
description: Okta Log Events
home: https://github.com/falcosecurity/plugins/tree/main/plugins/okta
keywords:
- audit
- log-events
- okta
license: Apache-2.0
maintainers:
- email: cncf-falco-dev@lists.cncf.io
name: The Falco Authors
sources:
- https://github.com/falcosecurity/plugins/tree/main/plugins/okta
- name: okta-rules
type: rulesfile
registry: ghcr.io
repository: falcosecurity/plugins/ruleset/okta
signature:
cosign:
certificate-oidc-issuer: https://token.actions.githubusercontent.com
certificate-oidc-issuer-regexp: ""
certificate-identity: ""
certificate-identity-regexp: https://github.com/LucaGuerra/cool-falco-ruleset/
certificate-github-workflow: ""
description: Okta Log Events
home: https://github.com/falcosecurity/plugins/tree/main/plugins/okta
keywords:
- audit
- log-events
- okta
- okta-rules
license: Apache-2.0
maintainers:
- email: cncf-falco-dev@lists.cncf.io
name: The Falco Authors
sources:
- https://github.com/falcosecurity/plugins/tree/main/plugins/okta/rules

View File

@ -1,271 +0,0 @@
# 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.
#
# The list of the data sources not allowed in plugins, since they are already
# used in Falco.
reserved_sources: ["syscall", "internal", "plugins"]
# The list of plugins officially recognized by the Falcosecurity organization.
# Registering your plugin here is required to reserve a given name, source, or id.
#
# License IDs refer to the SPDX License List at https://spdx.org/licenses
plugins:
- name: k8saudit
description: Read Kubernetes Audit Events and monitor Kubernetes Clusters
authors: The Falco Authors
contact: https://falco.org/community
maintainers:
- name: The Falco Authors
email: cncf-falco-dev@lists.cncf.io
keywords:
- audit
- audit-log
- audit-events
- kubernetes
url: https://github.com/falcosecurity/plugins/tree/main/plugins/k8saudit
rules_url: https://github.com/falcosecurity/plugins/tree/main/plugins/k8saudit/rules
license: Apache-2.0
capabilities:
sourcing:
supported: true
id: 1
source: k8s_audit
extraction:
supported: true
- name: cloudtrail
description: Reads Cloudtrail JSON logs from files/S3 and injects as events
authors: The Falco Authors
contact: https://falco.org/community
maintainers:
- name: The Falco Authors
email: cncf-falco-dev@lists.cncf.io
keywords:
- audit
- user-activity
- api-usage
- aws
url: https://github.com/falcosecurity/plugins/tree/main/plugins/cloudtrail
rules_url: https://github.com/falcosecurity/plugins/tree/main/plugins/cloudtrail/rules
license: Apache-2.0
capabilities:
sourcing:
supported: true
id: 2
source: aws_cloudtrail
extraction:
supported: true
- name: json
description: Extract values from any JSON payload
authors: The Falco Authors
contact: https://falco.org/community
maintainers:
- name: The Falco Authors
email: cncf-falco-dev@lists.cncf.io
keywords:
- json-events
- json-payload
- extractor
url: https://github.com/falcosecurity/plugins/tree/main/plugins/json
license: Apache-2.0
capabilities:
extraction:
supported: true
- name: dummy
description: Reference plugin used to document interface
authors: The Falco Authors
contact: https://falco.org/community
maintainers:
- name: The Falco Authors
email: cncf-falco-dev@lists.cncf.io
url: https://github.com/falcosecurity/plugins/tree/main/plugins/dummy
license: Apache-2.0
capabilities:
sourcing:
supported: true
id: 3
source: dummy
extraction:
supported: true
- name: dummy_c
description: Like dummy, but written in C++
authors: The Falco Authors
contact: https://falco.org/community
maintainers:
- name: The Falco Authors
email: cncf-falco-dev@lists.cncf.io
url: https://github.com/falcosecurity/plugins/tree/main/plugins/dummy_c
license: Apache-2.0
capabilities:
sourcing:
supported: true
id: 4
source: dummy_c
extraction:
supported: true
- name: docker
description: Docker Events
authors: Thomas Labarussias
contact: https://github.com/Issif
maintainers:
- name: Thomas Labarussias
email: issif_github@gadz.org
keywords:
- docker-events
url: https://github.com/Issif/docker-plugin
rules_url: https://github.com/Issif/docker-plugin/tree/main/rules
license: Apache-2.0
capabilities:
sourcing:
supported: true
id: 5
source: docker
extraction:
supported: true
- name: seccompagent
description: Seccomp Agent Events
authors: Alban Crequy
contact: https://github.com/kinvolk/seccompagent
url: https://github.com/kinvolk/seccompagent
keywords:
- seccomp
- kinvolk
license: Apache-2.0
capabilities:
sourcing:
supported: true
id: 6
source: seccompagent
extraction:
supported: true
- name: okta
description: Okta Log Events
authors: The Falco Authors
contact: https://falco.org/community
maintainers:
- name: The Falco Authors
email: cncf-falco-dev@lists.cncf.io
keywords:
- audit
- log-events
- okta
url: https://github.com/falcosecurity/plugins/tree/main/plugins/okta
rules_url: https://github.com/falcosecurity/plugins/tree/main/plugins/okta/rules
license: Apache-2.0
capabilities:
sourcing:
supported: true
id: 7
source: okta
extraction:
supported: true
signature:
cosign:
certificate-oidc-issuer: https://token.actions.githubusercontent.com
certificate-identity-regexp: https://github.com/LucaGuerra/cool-falco-ruleset/
- name: github
description: Github Webhook Events
authors: The Falco Authors
contact: https://falco.org/community
maintainers:
- name: The Falco Authors
email: cncf-falco-dev@lists.cncf.io
keywords:
- audit
- log-events
- webhook
- github-activity
- github
url: https://github.com/falcosecurity/plugins/tree/main/plugins/github
rules_url: https://github.com/falcosecurity/plugins/tree/main/plugins/github/rules
license: Apache-2.0
capabilities:
sourcing:
supported: true
id: 8
source: github
extraction:
supported: true
- name: k8saudit-eks
description: Read Kubernetes Audit Events from AWS EKS Clusters
authors: The Falco Authors
contact: https://falco.org/community
url: https://github.com/falcosecurity/plugins/tree/main/plugins/k8saudit-eks
rules_url: https://github.com/falcosecurity/plugins/tree/main/plugins/k8saudit/rules
license: Apache-2.0
keywords:
- audit
- audit-log
- audit-events
- kubernetes
- eks
- aws
capabilities:
sourcing:
supported: true
id: 9
source: k8s_audit
extraction:
supported: true
- name: nomad
description: Read Hashicorp Nomad Events Stream
authors: Alberto Llamas
contact: https://github.com/albertollamaso/nomad-plugin/issues
maintainers:
- name: Alberto Llamas
keywords:
- audit
- audit-events
- nomad
url: https://github.com/albertollamaso/nomad-plugin/tree/main
rules_url: https://github.com/albertollamaso/nomad-plugin/tree/main/rules
license: Apache-2.0
capabilities:
sourcing:
supported: true
id: 10
source: nomad
extraction:
supported: true
- name: dnscollector
description: DNS Collector Events
authors: Daniel Moloney
contact: https://github.com/SysdigDan/dnscollector-falco-plugin/issues
maintainers:
- name: Daniel Moloney
keywords:
- audit
- log-events
- dns
url: https://github.com/SysdigDan/dnscollector-falco-plugin
rules_url: https://github.com/SysdigDan/dnscollector-falco-plugin/tree/master/rules
license: Apache-2.0
capabilities:
sourcing:
supported: true
id: 11
source: dnscollector
extraction:
supported: true
- name: test
description: This ID is reserved for source plugin development. Any plugin author can use this ID, but authors can expect events from other developers with this ID. After development is complete, the author should request an actual ID
reserved: true
capabilities:
sourcing:
supported: true
id: 999
source: test

View File

@ -1,128 +0,0 @@
// 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 oci
import (
"errors"
"fmt"
"os"
"strings"
"github.com/falcosecurity/plugin-sdk-go/pkg/loader"
"github.com/falcosecurity/plugin-sdk-go/pkg/sdk/plugins"
"github.com/falcosecurity/falcoctl/pkg/oci"
"github.com/falcosecurity/plugins/build/registry/pkg/common"
)
// rulesFileConfig generates the artifact configuration for a rulesfile starting form the tar.gz archive,
// its name and version.
func rulesfileConfig(name, version, filePath string) (*oci.ArtifactConfig, error) {
// Create temp dir.
tmpDir, err := os.MkdirTemp("", "registry-oci-")
if err != nil {
return nil, fmt.Errorf("unable to create temporary dir while preparing to extract rulesfile %q: %v", filePath, err)
}
defer os.RemoveAll(tmpDir)
files, err := common.ExtractTarGz(filePath, tmpDir)
if err != nil {
return nil, err
}
cfg := &oci.ArtifactConfig{
Name: name,
Version: version,
Dependencies: nil,
Requirements: nil,
}
for _, file := range files {
// Get the requirements for the given file.
req, err := rulesfileRequirement(file)
if err != nil && !errors.Is(err, ErrReqNotFound) {
return nil, err
}
// If found add it to the requirements list.
if err == nil {
_ = cfg.SetRequirement(req.Name, req.Version)
}
deps, err := rulesfileDependencies(file)
if err != nil && !errors.Is(err, ErrDepNotFound) {
return nil, err
}
// If found add it to the dependencies list.
if err == nil {
for _, d := range deps {
_ = cfg.SetDependency(d.Name, d.Version, d.Alternatives)
}
}
}
if cfg.Dependencies == nil || cfg.Requirements == nil {
return nil, fmt.Errorf("no dependencies or requirements found for rulesfile %q", filePath)
}
return cfg, nil
}
func pluginConfig(name, version string, pluginInfo *plugins.Info) (*oci.ArtifactConfig, error) {
// Check that the name we got from the registry.yaml is the same as the embedded one in the plugin at build time.
if name != pluginInfo.Name {
return nil, fmt.Errorf("mismatch between name in registry.yaml (%q) and name found in plugin shared object (%q)", name, pluginInfo.Name)
}
cfg := &oci.ArtifactConfig{
Name: name,
Version: version,
Dependencies: nil,
Requirements: nil,
}
_ = cfg.SetRequirement(common.PluginAPIVersion, pluginInfo.RequiredAPIVersion)
return cfg, nil
}
func pluginInfo(filePath string) (*plugins.Info, error) {
// Create temp dir.
tmpDir, err := os.MkdirTemp("", "registry-oci-")
if err != nil {
return nil, fmt.Errorf("unable to create temporary dir while preparing to extract plugin %q: %v", filePath, err)
}
defer os.RemoveAll(tmpDir)
files, err := common.ExtractTarGz(filePath, tmpDir)
if err != nil {
return nil, err
}
for _, file := range files {
// skip files that are not a shared library such as README files.
if !strings.HasSuffix(file, ".so") {
continue
}
// Get the plugin info.
plugin, err := loader.NewPlugin(file)
if err != nil {
return nil, fmt.Errorf("unable to open plugin %q: %w", file, err)
}
return plugin.Info(), nil
}
return nil, fmt.Errorf("no plugin found in archive %q", filePath)
}

View File

@ -1,48 +0,0 @@
// 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.
*/
/*
Copyright (C) 2022 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 oci
const (
PluginNamespace = "plugins/plugin"
RulesfileNamespace = "plugins/ruleset"
RegistryToken = "REGISTRY_TOKEN"
RegistryUser = "REGISTRY_USER"
RegistryOCI = "REGISTRY"
RepoGithub = "REPO_GITHUB"
FalcoAuthors = "The Falco Authors"
PluginsRepo = "https://github.com/falcosecurity/plugins"
archiveSuffix = ".tar.gz"
amd64Platform = "linux/amd64"
arm64Platform = "linux/arm64"
)

View File

@ -1,83 +0,0 @@
// 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 oci
import (
"bufio"
"errors"
"fmt"
"os"
"strings"
"github.com/falcosecurity/falcoctl/pkg/oci"
"gopkg.in/yaml.v3"
)
const depsKey = "- required_plugin_versions"
// ErrDepNotFound error when the dependencies are not found in the rulesfile.
var ErrDepNotFound = errors.New("dependencies not found")
// rulesfileDependencies given a rulesfile in yaml format it scans it nad extracts its dependencies.
func rulesfileDependencies(fileName string) ([]oci.ArtifactDependency, error) {
var start bool
var buf []byte
var deps []oci.ArtifactDependency
// Open the file.
file, err := os.Open(fileName)
if err != nil {
return nil, fmt.Errorf("unable to open file %q: %v", fileName, file)
}
// Prepare the file to be read line by line.
fileScanner := bufio.NewScanner(file)
fileScanner.Split(bufio.ScanLines)
// Is appended to each line when inserted in the buffer.
newLine := []byte("\n")
// Falco rulesfiles are a list of dictionaries. We only want the "required plugin versions" by the ruleset. We do
// not want to load all the file in memory, so we scan it line by line. When we reach the interested section we save
// each line in a buffer, and after that we unmarshal it to a proper data structure.
for fileScanner.Scan() {
// If we have already found the section of interest, and we get a new item of the list then we stop.
if start {
if strings.HasPrefix(fileScanner.Text(), "-") {
break
} else {
buf = append(buf, fileScanner.Bytes()...)
buf = append(buf, newLine...)
}
} else {
if strings.HasPrefix(fileScanner.Text(), depsKey) {
start = true
}
}
}
if !start {
return nil, fmt.Errorf("dependencies for rulesfile %q: %w", fileName, ErrDepNotFound)
}
if err := yaml.Unmarshal(buf, &deps); err != nil {
return nil, fmt.Errorf("unable to unmarshal the required plugins versions: %w", err)
}
return deps, nil
}

View File

@ -1,423 +0,0 @@
// 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 oci
import (
"context"
"fmt"
"os"
"path/filepath"
"regexp"
"runtime"
"strings"
"github.com/falcosecurity/plugin-sdk-go/pkg/sdk/plugins"
"github.com/falcosecurity/plugins/build/registry/pkg/common"
"github.com/blang/semver"
"github.com/falcosecurity/falcoctl/pkg/oci"
"github.com/falcosecurity/falcoctl/pkg/oci/authn"
ocipusher "github.com/falcosecurity/falcoctl/pkg/oci/pusher"
"github.com/falcosecurity/plugins/build/registry/pkg/registry"
"k8s.io/klog/v2"
"oras.land/oras-go/v2/registry/remote"
"oras.land/oras-go/v2/registry/remote/auth"
)
type config struct {
// registryToken authentication token for the OCI registry.
registryToken string
// registryUser user used to interact with the OCI registry.
registryUser string
// registryHost hostname of the OCI registry.
registryHost string
// pluginsRepo the Ref of the git repository associated with the OCI artifacts.
pluginsRepo string
}
func lookupConfig() (*config, error) {
var found bool
cfg := &config{}
if cfg.registryToken, found = os.LookupEnv(RegistryToken); !found {
return nil, fmt.Errorf("environment variable with key %q not found, please set it before running this tool", RegistryToken)
}
if cfg.registryUser, found = os.LookupEnv(RegistryUser); !found {
return nil, fmt.Errorf("environment variable with key %q not found, please set it before running this tool", RegistryUser)
}
if cfg.registryHost, found = os.LookupEnv(RegistryOCI); !found {
return nil, fmt.Errorf("environment variable with key %q not found, please set it before running this tool", RegistryOCI)
}
if cfg.pluginsRepo, found = os.LookupEnv(RepoGithub); !found {
return nil, fmt.Errorf("environment variable with key %q not found, please set it before running this tool", RepoGithub)
}
return cfg, nil
}
// refFromPluginEntry returns an OCI reference for a plugin entry in the registry.yaml file.
func refFromPluginEntry(cfg *config, plugin *registry.Plugin, rulesFile bool) string {
var namespace string
// If the RulesURL field is set then the artifact is a rulesfile, otherwise a plugin.
if rulesFile {
namespace = RulesfileNamespace
} else {
namespace = PluginNamespace
}
// Build and return the artifact reference.
return filepath.Join(cfg.registryHost, cfg.registryUser, namespace, plugin.Name)
}
func currentPlatform() string {
return fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
}
// DoUpdateOCIRegistry publishes new plugins with related rules to be released.
// For each plugin in the registry index, it looks for new versions, since the latest version fetched from the remote OCI
// repository, as tags on the local Git repository.
// For each new version, it downloads the related plugin and rule set from the Falco distribution and updates the OCI
// repository accordingly.
func DoUpdateOCIRegistry(ctx context.Context, registryFile, pluginsAMD4, pluginsARM64, rulesfiles, devTag string) ([]registry.ArtifactPushMetadata, error) {
var (
cfg *config
err error
)
// Load the configuration from env variables.
if cfg, err = lookupConfig(); err != nil {
return nil, err
}
cred := &auth.Credential{
Username: cfg.registryUser,
Password: cfg.registryToken,
}
ociClient := authn.NewClient(authn.WithCredentials(cred))
reg, err := registry.LoadRegistryFromFile(registryFile)
if err != nil {
return nil, fmt.Errorf("an error occurred while loading registry entries from file %q: %v", registryFile, err)
}
artifacts := []registry.ArtifactPushMetadata{}
// For each plugin in the registry index, look for new ones to be released, and publish them.
for _, plugin := range reg.Plugins {
pa, ra, err := handleArtifact(ctx, cfg, &plugin, ociClient, pluginsAMD4, pluginsARM64, rulesfiles, devTag)
if err != nil {
return artifacts, err
}
artifacts = append(artifacts, pa...)
artifacts = append(artifacts, ra...)
// Clean up
if err := os.RemoveAll(plugin.Name); err != nil {
return artifacts, fmt.Errorf("unable to remove folder %q: %v", plugin.Name, err)
}
}
return artifacts, nil
}
func tagsFromVersion(version *semver.Version) []string {
var tags []string
// If we are not handling a release candidate then add floating tags.
if len(version.Pre) == 0 {
majorVer := fmt.Sprintf("%d", version.Major)
minorVer := fmt.Sprintf("%d.%d", version.Major, version.Minor)
fullVer := version.String()
tags = append(tags, "latest", majorVer, minorVer, fullVer)
} else {
tags = append(tags, version.String())
}
return tags
}
// handleArtifact it pushes artifacts related to a given plugin in the registry.yaml file.
// It could happen that for a given plugin no artifacts such as builds and rulesets are available.
// Consider the case when we release a single plugin.
func handleArtifact(ctx context.Context, cfg *config, plugin *registry.Plugin, ociClient remote.Client,
pluginsAMD64, pluginsARM64, rulesfiles, devTag string) ([]registry.ArtifactPushMetadata, []registry.ArtifactPushMetadata, error) {
// Filter out plugins that are not owned by falcosecurity.
if !strings.HasPrefix(plugin.URL, PluginsRepo) {
sepString := strings.Repeat("#", 15)
klog.Info("%s %s %s", sepString, plugin.Name, sepString)
klog.Infof("skipping plugin %q with authors %q: it is not maintained by %q",
plugin.Name, plugin.Authors, FalcoAuthors)
return nil, nil, nil
}
// Handle the plugin.
newPluginArtifacts, err := handlePlugin(ctx, cfg, plugin, ociClient, pluginsAMD64, pluginsARM64, devTag)
if err != nil {
return nil, nil, err
}
// Handle the rules.
newRuleArtifacts := []registry.ArtifactPushMetadata{}
if plugin.RulesURL != "" {
newRuleArtifacts, err = handleRule(ctx, cfg, plugin, ociClient, rulesfiles, devTag)
if err != nil {
return nil, nil, err
}
}
return newPluginArtifacts, newRuleArtifacts, nil
}
// handlePlugin for a given plugin it checks if there exists build artifacts in the given folders, and
// if found packs them as an OCI artifact and pushes them to the registry.
func handlePlugin(ctx context.Context, cfg *config, plugin *registry.Plugin, ociClient remote.Client,
pluginsAMD64, pluginsARM64 string, devTag string) ([]registry.ArtifactPushMetadata, error) {
var configLayer *oci.ArtifactConfig
var err error
var filepaths, platforms, tags []string
var version string
var infoP *plugins.Info
// Build the reference for the artifact.
ref := refFromPluginEntry(cfg, plugin, false)
// Metadata of the plugins OCI artifacts push.
metadata := []registry.ArtifactPushMetadata{}
// Get the name of the build object for the amd64 architecture.
amd64Build, err := buildName(plugin.Name, pluginsAMD64, false)
if err != nil {
return nil, err
}
if amd64Build != "" {
if infoP, err = pluginInfo(filepath.Join(pluginsAMD64, amd64Build)); err != nil {
return nil, err
}
// Check that the plugin has the same name as the one we got from the registry.yaml.
// If not, we skip it. It could happen that plugins share the same prefix, example k8saudit, k8saudit-gke.
if infoP.Name != plugin.Name {
// buildName func returned a wrong path starting from the plugin name found in registry.yaml.
klog.Warningf("skipping plugin since there is a mismatch in plugin name (%q) and plugin info name(%q)", plugin.Name, infoP.Name)
return nil, nil
}
filepaths = append(filepaths, filepath.Join(pluginsAMD64, amd64Build))
platforms = append(platforms, amd64Platform)
}
// Get the name of the build object for the arm64 architecture.
arm64Build, err := buildName(plugin.Name, pluginsARM64, false)
if err != nil {
return nil, err
}
if arm64Build != "" {
filepaths = append(filepaths, filepath.Join(pluginsARM64, arm64Build))
platforms = append(platforms, arm64Platform)
}
if arm64Build == "" && amd64Build == "" {
return nil, nil
}
sepString := strings.Repeat("#", 15)
klog.Infof("%s %s %s", sepString, plugin.Name, sepString)
// Extract version from build object.
klog.Infof("generating plugin's config layer")
version, tags, err = versionAndTags(plugin.Name, filepath.Base(filepaths[0]), devTag)
if err != nil {
return nil, err
}
if infoP == nil {
klog.Warningf("no config layer generated for plugin %q: the plugins has not been build for the current platform %q", plugin.Name, currentPlatform())
return nil, nil
}
configLayer, err = pluginConfig(plugin.Name, version, infoP)
if err != nil {
klog.Errorf("unable to generate config file: %v", err)
return nil, err
}
klog.Infof("pushing plugin to remote repo with ref %q and tags %q", ref, tags)
pusher := ocipusher.NewPusher(ociClient, false, nil)
res, err := pusher.Push(ctx, oci.Plugin, ref,
ocipusher.WithTags(tags...),
ocipusher.WithFilepathsAndPlatforms(filepaths, platforms),
ocipusher.WithArtifactConfig(*configLayer),
ocipusher.WithAnnotationSource(cfg.pluginsRepo))
if err != nil {
return nil, fmt.Errorf("an error occurred while pushing plugin %q: %w", plugin.Name, err)
}
if res != nil {
metadata = append(metadata, registry.ArtifactPushMetadata{
registry.RepositoryMetadata{
Ref: ref,
},
registry.ArtifactMetadata{
Digest: res.RootDigest,
Tags: tags,
},
})
}
return metadata, nil
}
// handleRule for a given plugin it checks if there exists rulesfiles in the given folder, and
// if found packs them as an OCI artifact and pushes it to the registry.
func handleRule(ctx context.Context, cfg *config, plugin *registry.Plugin,
ociClient remote.Client, rulesfiles, devTag string) ([]registry.ArtifactPushMetadata, error) {
var err error
var filepaths, tags []string
var version string
// Build the reference for the artifact.
ref := refFromPluginEntry(cfg, plugin, true)
// Metadata of the plugins OCI artifacts push.
metadata := []registry.ArtifactPushMetadata{}
// Get the name of the build object for the amd64 architecture.
rulesfileBuild, err := buildName(plugin.Name, rulesfiles, true)
if err != nil {
return nil, err
}
if rulesfileBuild != "" {
filepaths = append(filepaths, filepath.Join(rulesfiles, rulesfileBuild))
} else {
return nil, nil
}
sepString := strings.Repeat("#", 15)
klog.Infof("%s %s %s", sepString, rulesfileNameFromPlugin(plugin.Name), sepString)
klog.Infof("generating rulesfile's config layer")
version, tags, err = versionAndTags(plugin.Name, filepath.Base(filepaths[0]), devTag)
if err != nil {
return nil, err
}
configLayer, err := rulesfileConfig(rulesfileNameFromPlugin(plugin.Name), version, filepaths[0])
if err != nil {
klog.Errorf("unable to generate config file: %v", err)
return nil, err
}
klog.Infof("pushing rulesfile to remote repo with ref %q and tags %q", ref, tags)
pusher := ocipusher.NewPusher(ociClient, false, nil)
res, err := pusher.Push(ctx, oci.Rulesfile, ref,
ocipusher.WithTags(tags...),
ocipusher.WithFilepaths(filepaths),
ocipusher.WithArtifactConfig(*configLayer),
ocipusher.WithAnnotationSource(cfg.pluginsRepo))
if err != nil {
return nil, fmt.Errorf("an error occurred while pushing rulesfile %q: %w", plugin.Name, err)
}
if res != nil {
metadata = append(metadata, registry.ArtifactPushMetadata{
registry.RepositoryMetadata{
Ref: ref,
},
registry.ArtifactMetadata{
Digest: res.RootDigest,
Tags: tags,
},
})
}
return metadata, nil
}
func rulesfileNameFromPlugin(name string) string {
return fmt.Sprintf("%s%s", name, common.RulesArtifactSuffix)
}
// buildName returns the name of the build object for a given object name.
// It searches in the given folder if build artifact exists that has the same
// prefix as the object. If we are searching for a rulesfiles object then, the
// rulefiles variable needs to be set to true.
func buildName(objName, dirPath string, rulesfile bool) (string, error) {
if dirPath == "" {
return "", nil
}
// Get the entries
entries, err := os.ReadDir(dirPath)
if err != nil {
return "", fmt.Errorf("unable to get build object for %q: %w", objName, err)
}
for _, entry := range entries {
name := entry.Name()
if rulesfile {
if strings.HasPrefix(name, objName+"-rules") {
return name, nil
}
} else {
if strings.HasPrefix(name, objName) && !strings.Contains(name, "rules") {
return name, nil
}
}
}
return "", nil
}
func versionAndTags(pluginName, buildName, devTag string) (string, []string, error) {
var version string
var tags []string
var err error
if strings.Contains(buildName, "-rules") {
version = strings.TrimPrefix(buildName, pluginName+"-rules-")
version = strings.TrimSuffix(version, archiveSuffix)
} else {
regexPattern := `\b-linux\S*`
regex := regexp.MustCompile(regexPattern)
// Replace all substrings starting with "linux" with an empty string
version = regex.ReplaceAllString(buildName, "")
version = strings.TrimPrefix(version, pluginName+"-")
}
if devTag != "" {
return version, append(tags, devTag), nil
}
// If not a dev version, we expect to but be semver compatible.
semVer, err := semver.Parse(version)
if err != nil {
return "", nil, fmt.Errorf("unable to parse version for %q: %w", buildName, err)
}
return version, tagsFromVersion(&semVer), nil
}

View File

@ -1,36 +0,0 @@
// 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 oci_test
import (
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
const (
samplePluginRepoRef = "ghcr.io/falcosecurity/plugins/plugins/k8saudit"
sampleDigest = "sha256:454b5d97ecbb71c8b605af2028f12fc2c792e363b150b1aeeb773c802699d647"
samplePluginTag = "1.0.0"
)
func TestOCI(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "OCI Suite")
}

View File

@ -1,37 +0,0 @@
// 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 oci
import (
"encoding/json"
"io"
"github.com/pkg/errors"
"github.com/falcosecurity/plugins/build/registry/pkg/registry"
)
func PrintUpdateStatus(newArtifacts registry.ArtifactsPushStatus, output io.Writer) error {
bytes, err := json.Marshal(newArtifacts)
if err != nil {
return errors.Wrap(err, "error marshaling oci registry push metadata")
}
output.Write(bytes)
return nil
}

View File

@ -1,93 +0,0 @@
// 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 oci_test
import (
"bytes"
"encoding/json"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/falcosecurity/plugins/build/registry/internal/options"
"github.com/falcosecurity/plugins/build/registry/pkg/oci"
"github.com/falcosecurity/plugins/build/registry/pkg/registry"
)
var _ = Describe("Update OCI registry", func() {
var (
out *bytes.Buffer
status registry.ArtifactsPushStatus
opts *options.CommonOptions
err error
)
Context("Print update result", func() {
BeforeEach(func() {
out = new(bytes.Buffer)
opts = options.NewCommonOptions(options.WithOutput(out))
})
When("at least one artifact has been pushed to the OCI registry", func() {
BeforeEach(func() {
status = registry.ArtifactsPushStatus{
{
Repository: registry.RepositoryMetadata{
Ref: samplePluginRepoRef,
},
Artifact: registry.ArtifactMetadata{
Digest: sampleDigest,
Tags: []string{samplePluginTag}},
},
}
err = oci.PrintUpdateStatus(status, opts.Output)
})
It("should not fail", func() {
Expect(err).To(BeNil())
})
It("output should not be empty", func() {
Expect(out.String()).ToNot(BeEmpty())
})
It("output should contain a valid JSON", func() {
status = registry.ArtifactsPushStatus{}
err := json.Unmarshal(out.Bytes(), &status)
Expect(err).To(BeNil())
})
})
When("no artifacts have been pushed to the OCI registry", func() {
BeforeEach(func() {
status = registry.ArtifactsPushStatus{}
err = oci.PrintUpdateStatus(status, opts.Output)
})
It("should not fail", func() {
Expect(err).To(BeNil())
})
It("output should not be empty", func() {
Expect(out.String()).ToNot(BeEmpty())
})
It("output should contain a valid JSON", func() {
status = registry.ArtifactsPushStatus{}
err := json.Unmarshal(out.Bytes(), &status)
Expect(err).To(BeNil())
})
})
})
})

View File

@ -1,88 +0,0 @@
// 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 oci
import (
"bufio"
"errors"
"fmt"
"os"
"strconv"
"strings"
"github.com/blang/semver"
"github.com/falcosecurity/falcoctl/pkg/oci"
"github.com/falcosecurity/plugins/build/registry/pkg/common"
)
const (
rulesEngineAnchor = "- required_engine_version"
)
// ErrReqNotFound error when the requirements are not found in the rulesfile.
var ErrReqNotFound = errors.New("requirements not found")
// rulesfileRequirement given a rulesfile in yaml format it scans it and extracts its requirements.
func rulesfileRequirement(filePath string) (*oci.ArtifactRequirement, error) {
var requirement string
// Open the file.
file, err := os.Open(filePath)
if err != nil {
return nil, fmt.Errorf("unable to open file %q: %v", filePath, file)
}
defer file.Close()
// Prepare the file to be read line by line.
fileScanner := bufio.NewScanner(file)
fileScanner.Split(bufio.ScanLines)
for fileScanner.Scan() {
if strings.HasPrefix(fileScanner.Text(), rulesEngineAnchor) {
requirement = fileScanner.Text()
break
}
}
if requirement == "" {
return nil, fmt.Errorf("requirements for rulesfile %q: %w", filePath, ErrReqNotFound)
}
// Split the requirement and parse the version to semVer.
// In case the requirement was expressed as a numeric value,
// we convert it to semver and treat it as minor version.
tokens := strings.Split(fileScanner.Text(), ":")
version := strings.TrimSpace(tokens[1])
reqVer, err := semver.Parse(version)
if err != nil {
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: common.EngineVersionKey,
Version: reqVer.String(),
}, nil
}

View File

@ -1,39 +0,0 @@
// 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 oci
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestRulesfileRequirement(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)
}

View File

@ -1 +0,0 @@
- required_engine_version: test

View File

@ -1 +0,0 @@
- required_engine_version: 15

View File

@ -1 +0,0 @@
- required_engine_version: 0.31.0

View File

@ -1,78 +0,0 @@
// 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 registry
import (
"io"
"gopkg.in/yaml.v2"
)
type encoder interface {
Encode(io.Writer) error
}
// Encode writes the content to a io.Writer
func (r *SourcingCapability) Encode(w io.Writer) error {
return yaml.NewEncoder(w).Encode(r)
}
// Encode writes the content to a io.Writer
func (r *ExtractionCapability) Encode(w io.Writer) error {
return yaml.NewEncoder(w).Encode(r)
}
// Encode writes the content to a io.Writer
func (r *Capabilities) Encode(w io.Writer) error {
return yaml.NewEncoder(w).Encode(r)
}
// Encode writes the content to a io.Writer
func (r *Plugin) Encode(w io.Writer) error {
return yaml.NewEncoder(w).Encode(r)
}
// Encode writes the content to a io.Writer
func (r *Registry) Encode(w io.Writer) error {
return yaml.NewEncoder(w).Encode(r)
}
// Decode fills the structure by reading from a io.Reader
func (r *SourcingCapability) Decode(w io.Reader) error {
return yaml.NewDecoder(w).Decode(r)
}
// Decode fills the structure by reading from a io.Reader
func (r *ExtractionCapability) Decode(w io.Reader) error {
return yaml.NewDecoder(w).Decode(r)
}
// Decode fills the structure by reading from a io.Reader
func (r *Capabilities) Decode(w io.Reader) error {
return yaml.NewDecoder(w).Decode(r)
}
// Decode fills the structure by reading from a io.Reader
func (r *Plugin) Decode(w io.Reader) error {
return yaml.NewDecoder(w).Decode(r)
}
// Decode fills the structure by reading from a io.Reader
func (r *Registry) Decode(w io.Reader) error {
return yaml.NewDecoder(w).Decode(r)
}

View File

@ -1,40 +0,0 @@
// 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 registry
import (
"io"
"os"
)
// LoadRegistryFromFile loads the registry from a file on disk.
func LoadRegistryFromFile(fname string) (*Registry, error) {
file, err := os.Open(fname)
if err != nil {
return nil, err
}
defer file.Close()
return load(file)
}
// load reads from a io.Reader and uses the content to populate and
// return a new instance of Registry
func load(r io.Reader) (*Registry, error) {
registry := &Registry{}
return registry, registry.Decode(r)
}

View File

@ -1,56 +0,0 @@
// 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 registry
import (
"bytes"
)
func encodeString(r encoder) string {
buf := bytes.Buffer{}
err := r.Encode(&buf)
if err != nil {
return "string encoding error: " + err.Error()
}
return buf.String()
}
// String implements the fmt.Stringer interface
func (r *SourcingCapability) String() string {
return encodeString(r)
}
// String implements the fmt.Stringer interface
func (r *ExtractionCapability) String() string {
return encodeString(r)
}
// String implements the fmt.Stringer interface
func (r *Capabilities) String() string {
return encodeString(r)
}
// String implements the fmt.Stringer interface
func (r *Plugin) String() string {
return encodeString(r)
}
// String implements the fmt.Stringer interface
func (r *Registry) String() string {
return encodeString(r)
}

View File

@ -1,83 +0,0 @@
// 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 registry
import (
"github.com/falcosecurity/falcoctl/pkg/index/index"
)
// MaxPublicID represents the max allowed value for plublic plugin IDs,
// see https://github.com/falcosecurity/plugins/blob/main/docs/plugin-ids.md
const MaxPublicID = 1<<30 - 1 // 1073741823 is the public block upper limit
type SourcingCapability struct {
Supported bool `yaml:"supported"`
ID uint `yaml:"id"`
Source string `yaml:"source"`
}
type ExtractionCapability struct {
Supported bool `yaml:"supported"`
Sources []string `yaml:"sources"`
}
type Capabilities struct {
Sourcing SourcingCapability `yaml:"sourcing"`
Extraction ExtractionCapability `yaml:"extraction"`
}
type Plugin struct {
Name string `yaml:"name"`
Description string `yaml:"description"`
Authors string `yaml:"authors"`
Contact string `yaml:"contact"`
Maintainers []struct {
Email string `yaml:"email"`
Name string `yaml:"name"`
} `yaml:"maintainers"`
Keywords []string `yaml:"keywords"`
URL string `yaml:"url"`
RulesURL string `yaml:"rules_url"`
License string `yaml:"license"`
Reserved bool `yaml:"reserved"`
Capabilities Capabilities `yaml:"capabilities"`
Signature *index.Signature `yaml:"signature,omitempty"`
}
type Registry struct {
Plugins []Plugin `yaml:"plugins"`
ReservedSources []string `yaml:"reserved_sources"`
}
type ArtifactsPushStatus []ArtifactPushMetadata
// ArtifactPushMetadata represents metadata of a push of an OCI Artifact to
// an OCI repository.
type ArtifactPushMetadata struct {
Repository RepositoryMetadata `json:"repository"`
Artifact ArtifactMetadata `json:"artifact"`
}
type ArtifactMetadata struct {
Digest string `json:"digest"`
Tags []string `json:"tags"`
}
type RepositoryMetadata struct {
Ref string `json:"ref"`
}

View File

@ -1,77 +0,0 @@
// 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 registry
import (
"fmt"
"regexp"
)
var (
rgxName = regexp.MustCompile(`^[a-z]+[a-z0-9-_]*$`)
rgxSource = regexp.MustCompile(`^[a-z]+[a-z0-9_]*$`)
)
func (s *SourcingCapability) validate(usedIDs map[uint]bool, forbiddenSources map[string]bool) error {
if s.Supported {
if s.ID > MaxPublicID {
return fmt.Errorf("source ID outside the allowed range (%d): '%d'", MaxPublicID, s.ID)
}
if _, ok := usedIDs[s.ID]; ok {
return fmt.Errorf("source ID is not unique: '%d'", s.ID)
}
// ID=0 is a special case and we don't want to define a source name
if s.ID != 0 {
if _, ok := forbiddenSources[s.Source]; ok {
return fmt.Errorf("forbidden source name: '%s'", s.Source)
}
if !rgxSource.MatchString(s.Source) {
return fmt.Errorf("source name does follow the naming convention: '%s'", s.Source)
}
}
usedIDs[s.ID] = true
}
return nil
}
// Validates returns nil if the Registry is valid, and an error otherwise.
// For more details regarding which constraints are checked for validation,
// refer to: https://github.com/falcosecurity/plugins#registering-a-new-plugin
func (r *Registry) Validate() error {
forbiddenSources := make(map[string]bool)
for _, s := range r.ReservedSources {
forbiddenSources[s] = true
}
ids := make(map[uint]bool)
names := make(map[string]bool)
for _, p := range r.Plugins {
if !rgxName.MatchString(p.Name) {
return fmt.Errorf("plugin name does follow the naming convention: '%s'", p.Name)
}
if _, ok := names[p.Name]; ok {
return fmt.Errorf("plugin name is not unique: '%s'", p.Name)
}
if err := p.Capabilities.Sourcing.validate(ids, forbiddenSources); err != nil {
return err
}
names[p.Name] = true
}
return nil
}

View File

@ -1,125 +0,0 @@
// 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 table
import (
"fmt"
"os"
"strings"
"github.com/falcosecurity/plugins/build/registry/pkg/registry"
)
func DoTable(registryFile, subFile, subTag string) error {
r, err := registry.LoadRegistryFromFile(registryFile)
if err != nil {
return err
}
err = r.Validate()
if err != nil {
return err
}
table, err := formatMarkdownTable(r)
if err != nil {
return err
}
if len(subFile) == 0 {
fmt.Println(table)
} else {
if len(subTag) == 0 {
return fmt.Errorf("subtag flag is required")
}
content, err := os.ReadFile(subFile)
if err != nil {
return err
}
pieces := strings.SplitN(string(content), subTag, 3)
if len(pieces) != 3 {
return fmt.Errorf("can't find two instances of subtag in text file: '%s'", subTag)
}
contentStr := fmt.Sprintf("%s%s\n%s\n%s%s", pieces[0], subTag, table, subTag, pieces[2])
if err = os.WriteFile(subFile, []byte(contentStr), 0666); err != nil {
return err
}
}
return nil
}
func formatMarkdownTable(r *registry.Registry) (string, error) {
var ret strings.Builder
ret.WriteString("| Name | Capabilities | Description\n")
ret.WriteString("| --- | --- | --- |\n")
for _, p := range r.Plugins {
line := fmt.Sprintf("| %s | %s | %s <br/><br/> Authors: %s <br/> License: %s |\n",
formatMarkdownStringWithURL(r, p.Name, p.URL),
formatMarkdownCapabilities(r, &p.Capabilities),
formatMarkdownStringNotAvailable(r, p.Description),
formatMarkdownStringWithURL(r, p.Authors, p.Contact),
formatMarkdownStringNotAvailable(r, p.License),
)
ret.WriteString(line)
}
return ret.String(), nil
}
func formatMarkdownCapabilities(r *registry.Registry, caps *registry.Capabilities) string {
var ret strings.Builder
if caps.Sourcing.Supported {
ret.WriteString(fmt.Sprintf("**Event Sourcing** <br/>ID: %d <br/>`%s`",
caps.Sourcing.ID,
caps.Sourcing.Source,
))
}
if caps.Extraction.Supported {
if ret.Len() > 0 {
ret.WriteString(" <br/>")
}
ret.WriteString("**Field Extraction** <br/> ")
if len(caps.Extraction.Sources) == 0 {
if caps.Sourcing.Supported {
ret.WriteString("`" + caps.Sourcing.Source + "`")
} else {
ret.WriteString("*All Sources*")
}
} else {
var sources []string
for _, s := range caps.Extraction.Sources {
sources = append(sources, "`"+s+"`")
}
ret.WriteString(strings.Join(sources, ", "))
}
}
return ret.String()
}
func formatMarkdownStringNotAvailable(r *registry.Registry, s string) string {
if len(s) == 0 {
return "N/A"
}
return s
}
func formatMarkdownStringWithURL(r *registry.Registry, s, url string) string {
if len(url) == 0 {
return formatMarkdownStringNotAvailable(r, s)
}
return fmt.Sprintf("[%s](%s)", formatMarkdownStringNotAvailable(r, s), url)
}

125
build/registry/registry.go Normal file
View File

@ -0,0 +1,125 @@
/*
Copyright (C) 2022 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 (
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/spf13/cobra"
)
const (
defaultTableSubTag = "<!-- REGISTRY -->"
)
func loadRegistryFromFile(fname string) (*Registry, error) {
file, err := os.Open(fname)
if err != nil {
return nil, err
}
defer file.Close()
return LoadRegistry(file)
}
func doCheck(fileName string) error {
registry, err := loadRegistryFromFile(fileName)
if err != nil {
return err
}
return registry.Check()
}
func doTable(registryFile, subFile, subTag, contentType string) error {
registry, err := loadRegistryFromFile(registryFile)
if err != nil {
return err
}
err = registry.Check()
if err != nil {
return err
}
table, err := registry.FormatMarkdownTable(contentType)
if err != nil {
return err
}
if len(subFile) == 0 {
fmt.Println(table)
} else {
if len(subTag) == 0 {
return fmt.Errorf("subtag flag is required")
}
content, err := ioutil.ReadFile(subFile)
if err != nil {
return err
}
pieces := strings.SplitN(string(content), subTag, 3)
if len(pieces) != 3 {
return fmt.Errorf("can't find two instances of subtag in text file: '%s'", subTag)
}
contentStr := fmt.Sprintf("%s%s\n%s\n%s%s", pieces[0], subTag, table, subTag, pieces[2])
if err = ioutil.WriteFile(subFile, []byte(contentStr), 0666); err != nil {
return err
}
}
return nil
}
func main() {
checkCmd := &cobra.Command{
Use: "check <filename>",
Short: "Verify the correctness of a plugin registry YAML file",
Args: cobra.ExactArgs(1),
DisableFlagsInUseLine: true,
RunE: func(c *cobra.Command, args []string) error {
return doCheck(args[0])
},
}
var tableSubFileName string
var tableSubTab string
var tableType string
tableCmd := &cobra.Command{
Use: "table <filename>",
Short: "Format a plugin registry YAML file in a MarkDown table",
Args: cobra.ExactArgs(1),
RunE: func(c *cobra.Command, args []string) error {
return doTable(args[0], tableSubFileName, tableSubTab, tableType)
},
}
tableFlags := tableCmd.Flags()
tableFlags.StringVar(&tableSubTab, "subtag", defaultTableSubTag, "A tag that delimits the start and the end of the text section to substitute with the generated table.")
tableFlags.StringVar(&tableSubFileName, "subfile", "", "If specified, the table will be written inside the file at this path, inserting it between the first two instances of the substitution tag.")
tableFlags.StringVar(&tableType, "type", sourcePluginsTableContentType, "The type of content to be included in the table")
rootCmd := &cobra.Command{
Use: "registry",
Version: "0.1.0",
}
rootCmd.AddCommand(checkCmd)
rootCmd.AddCommand(tableCmd)
if err := rootCmd.Execute(); err != nil {
fmt.Printf("error: %s\n", err)
os.Exit(1)
}
}

81
build/registry/table.go Normal file
View File

@ -0,0 +1,81 @@
/*
Copyright (C) 2022 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 (
"fmt"
"strings"
)
var (
sourcePluginsTableContentType = "plugins-source"
extractorPluginsTableContentType = "plugins-extractor"
)
func (r *Registry) FormatMarkdownTable(contentType string) (string, error) {
var ret strings.Builder
wrapNotAvailable := func(s string) string {
if len(s) == 0 {
return "N/A"
}
return s
}
formatWithURL := func(s string, url string) string {
if len(url) == 0 {
return wrapNotAvailable(s)
}
return fmt.Sprintf("[%s](%s)", wrapNotAvailable(s), url)
}
switch contentType {
case sourcePluginsTableContentType:
ret.WriteString("| ID | Name | Event Source | Description | Info |\n")
ret.WriteString("| --- | --- | --- | --- | --- |\n")
for _, s := range r.Plugins.Source {
line := fmt.Sprintf("| %d | %s | `%s` | %s | Authors: %s <br/> License: %s |\n",
s.ID,
formatWithURL(s.Name, s.URL),
wrapNotAvailable(s.Source),
wrapNotAvailable(s.Description),
formatWithURL(s.Authors, s.Contact),
wrapNotAvailable(s.License),
)
ret.WriteString(line)
}
case extractorPluginsTableContentType:
ret.WriteString("| Name | Extract Event Sources | Description | Info |\n")
ret.WriteString("| --- | --- | --- | --- |\n")
for _, e := range r.Plugins.Extractor {
sources := make([]string, 0)
for _, s := range e.Sources {
sources = append(sources, fmt.Sprintf("`%s`", s))
}
line := fmt.Sprintf("| %s | %s | %s | Authors: %s <br/> License: %s |\n",
formatWithURL(e.Name, e.URL),
wrapNotAvailable(strings.Join(sources, ", ")),
wrapNotAvailable(e.Description),
formatWithURL(e.Authors, e.Contact),
wrapNotAvailable(e.License),
)
ret.WriteString(line)
}
default:
return "", fmt.Errorf("unknown table content type: %s", contentType)
}
return ret.String(), nil
}

65
build/registry/types.go Normal file
View File

@ -0,0 +1,65 @@
/*
Copyright (C) 2022 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 (
"io"
"github.com/go-yaml/yaml"
)
type Source struct {
ID uint `yaml:"id"`
Source string `yaml:"source"`
Name string `yaml:"name"`
Description string `yaml:"description"`
Authors string `yaml:"authors"`
Contact string `yaml:"contact"`
URL string `yaml:"url"`
License string `yaml:"license"`
Reserved bool `yaml:"reserved"`
}
type Extractor struct {
Sources []string `yaml:"sources"`
Name string `yaml:"name"`
Description string `yaml:"description"`
Authors string `yaml:"authors"`
Contact string `yaml:"contact"`
URL string `yaml:"url"`
License string `yaml:"license"`
Reserved bool `yaml:"reserved"`
}
type Plugins struct {
Source []Source `yaml:"source"`
Extractor []Extractor `yaml:"extractor"`
}
type Registry struct {
Plugins Plugins `yaml:"plugins"`
ReservedSources []string `yaml:"reserved_sources"`
}
func LoadRegistry(r io.Reader) (*Registry, error) {
decoder := yaml.NewDecoder(r)
registry := &Registry{}
if err := decoder.Decode(registry); err != nil {
return nil, err
}
return registry, nil
}

View File

@ -1,2 +1 @@
version
utils
version

View File

@ -1,6 +1,5 @@
# SPDX-License-Identifier: Apache-2.0
#
# Copyright (C) 2023 The Falco Authors.
# Copyright (C) 2022 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

View File

@ -2,4 +2,4 @@ module github.com/falcosecurity/plugins/build/utils
go 1.17
require github.com/spf13/pflag v1.0.6
require github.com/spf13/pflag v1.0.5

View File

@ -1,2 +1,2 @@
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=

49
build/utils/version.go Executable file → Normal file
View File

@ -1,9 +1,8 @@
// SPDX-License-Identifier: Apache-2.0
//go:build (linux && cgo) || (darwin && cgo) || (freebsd && cgo)
// +build linux,cgo darwin,cgo freebsd,cgo
/*
Copyright (C) 2023 The Falco Authors.
Copyright (C) 2022 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.
@ -43,7 +42,7 @@ import (
#include <stdio.h>
static uintptr_t pluginOpen(const char* path, char** err) {
void* h = dlopen(path, RTLD_NOW|RTLD_GLOBAL|RTLD_DEEPBIND);
void* h = dlopen(path, RTLD_NOW|RTLD_GLOBAL);
if (h == NULL) {
*err = (char*)dlerror();
}
@ -75,9 +74,26 @@ static char* get_version(uintptr_t h, char** err) {
*/
import "C"
var rgxVersion = regexp.MustCompile(`^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?$`)
var rgxHash = regexp.MustCompile(`^[0-9a-z]+$`)
var rgxName = regexp.MustCompile(`^[a-z]+[a-z0-9_\-]*$`)
var rgxVersion *regexp.Regexp
var rgxHash *regexp.Regexp
var rgxName *regexp.Regexp
func init() {
var err error
// see: https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
rgxVersion, err = regexp.Compile(`^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?$`)
if err != nil {
panic(err.Error())
}
rgxHash, err = regexp.Compile(`^[0-9a-z]+$`)
if err != nil {
panic(err.Error())
}
rgxName, err = regexp.Compile(`^[a-z]+[a-z0-9_]*$`)
if err != nil {
panic(err.Error())
}
}
func pluginInfo(path string) (name, version string, err error) {
path, err = filepath.Abs(path)
@ -99,19 +115,14 @@ func pluginInfo(path string) (name, version string, err error) {
err = errors.New("cannot get name of " + path + ": " + C.GoString(cErr))
return
}
// we need to convert immediately the pointer into a string
// otherwise the pointer could change see plugin sdk cpp
name = C.GoString(cName)
cVer := C.get_version(h, &cErr)
if cVer == nil {
err = errors.New("cannot get version of " + path + ": " + C.GoString(cErr))
return
}
// Same as before
version = C.GoString(cVer)
return
return C.GoString(cName), C.GoString(cVer), nil
}
func git(args ...string) (output []string, err error) {
@ -163,18 +174,13 @@ func main() {
var hash string
// get last tag
// It matches the old tag in "pluginName-version" or the new one "plugins/pluginName/semver"
tags, err := git("describe", "--tags", "--abbrev=0", "--match", name+`-[0-9]*`, "--match", "plugins/"+name+"/v*")
tags, err := git("describe", "--tags", "--abbrev=0", "--match", name+`-*`)
if err == nil {
if len(tags) == 0 {
fail(errors.New("no git tag found for: " + name))
}
lastTag := tags[0]
if strings.HasPrefix(lastTag, name) {
lastVer = strings.Replace(lastTag, name+"-", "", 1)
} else {
lastVer = strings.Replace(lastTag, "plugins/"+name+"/v", "", 1)
}
lastVer = strings.Replace(lastTag, name+"-", "", 1)
if !rgxVersion.MatchString(lastVer) {
fail(errors.New("plugin latest released version not compatible with SemVer: " + lastTag))
}
@ -205,14 +211,11 @@ func main() {
} else {
// stable versions MUST have a precise tag matching plugin name and version
expectedTag := "plugins/" + name + "/v" + version
expectedTag := name + "-" + version
tags, err := git("--no-pager", "tag", "--points-at", "HEAD")
if err != nil {
fail(err)
}
if len(tags) == 0 || len(tags[0]) == 0 {
fail(errors.New("there are no tags pointing at HEAD"))
}
for _, tag := range tags {
if tag == expectedTag {
fmt.Println(version)

View File

@ -1,76 +0,0 @@
#!/bin/bash
# SPDX-License-Identifier: Apache-2.0
#
# Copyright (C) 2025 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.
#
plugin=$1
if [ -z "$plugin" ]; then
echo "Usage changelog-gen.sh <plugin_name>"
exit 1
fi
tool=./build/changelog/bin/changelog
to=""
from=""
tags="$(git tag -l | grep -E -e ${plugin}-[0-9]+.[0-9]+.[0-9]+ -e ${plugin}/v[0-9]+.[0-9]+.[0-9]+ | grep -E -v ${plugin}-[0-9]+.[0-9]+.[0-9]+-rc | sort -V -r)"
# print title
echo "# Changelog"
echo ""
# generate entry for upcoming tag, if any
head="$(git rev-parse HEAD)"
dev_changelog="$(${tool} --from="" --to=${head} --plugin=${plugin})"
if [ ! -z "$dev_changelog" ]; then
echo "## dev build (unreleased)"
echo ""
echo "$dev_changelog"
echo ""
fi
# generate entry for each tag
for tag in $tags
do
from=$tag
if [ ! -z "$to" ]; then
ver=""
# support both the old and new tag formats
if [[ $to == plugins/* ]]; then
ver="$(echo ${to} | sed -e s/^plugins\\/${plugin}\\///)"
else
ver="$(echo ${to} | sed -e s/^${plugin}-// -e s/^/v/)"
fi
echo "## ${ver}"
echo ""
${tool} --from=${from} --to=${to} --plugin=${plugin}
echo ""
fi
to=$tag
done
# generate last entry for first tag, starting from the first commit
if [ -n "$to" ]; then
from="$(git rev-list --max-parents=0 HEAD)"
# support both the old and new tag formats
if [[ $to == plugins/* ]]; then
ver="$(echo ${to} | sed -e s/^plugins\\/${plugin}\\///)"
else
ver="$(echo ${to} | sed -e s/^${plugin}-// -e s/^/v/)"
fi
echo "## ${ver}"
echo ""
${tool} --from=${from} --to=${to} --plugin=${plugin}
echo ""
fi

View File

@ -2,69 +2,14 @@
This page summarizes some best practices and guidelines that can be useful to developers that are getting started with the [plugin system of Falco](https://falco.org/docs/plugins/). The [Developers Guide](https://falco.org/docs/plugins/developers-guide/) is mostly focused on the technical aspects of plugin development. In contrast, here we provide some guidance on more high-level points that may occur during the design and implementation phases.
## Plugin Directory Structure
Currently, Go is the most used language for writing plugins. So, below you can find the recommended layout for Go plugin projects. For other languages, you can adapt the layout accordingly.
### `/pkg`
Reusable Go packages that other plugins or projects can use. This directory is not mandatory but is highly recommended.
### `/plugin`
This directory contains the plugin entry point. This directory should have only one `.go` file, named as your plugin. This file must define the `main` package (and an empty `main()` function) per CGO requirement.
Usually, this file also imports packages from `/pkg` and defines an `init()` function to register the plugin capabilities (that's required if you are using the [plugin-go-sdk](https://github.com/falcosecurity/plugin-sdk-go)).
### `/rules`
This directory is optional. If you want to distribute rules files for your plugin, you can put them in this directory.
The building system of this repository will automatically build and publish them as a `.tar.gz` archive under [https://download.falco.org/?prefix=plugins/](https://download.falco.org/?prefix=plugins/).
### `/Makefile`
Providing a `Makefile` is mandatory for plugins hosted by this repository. The building system of this repository will use:
- `make` to build the plugin binary
- `make clean` to clean the built artifacts
- `make rules` to build the rules files (this is optional)
Below you can find an example of a typical `Makefile` for a plugin hosted by this repository.
```Makefile
SHELL=/bin/bash -o pipefail
GO ?= go
NAME := <YOUR-PLUGIN-NAME-HERE>
OUTPUT := lib$(NAME).so
ifeq ($(DEBUG), 1)
GODEBUGFLAGS= GODEBUG=cgocheck=1
else
GODEBUGFLAGS= GODEBUG=cgocheck=0
endif
all: $(OUTPUT)
clean:
@rm -f *.so *.h
$(OUTPUT):
@$(GODEBUGFLAGS) $(GO) build -buildmode=c-shared -o $(OUTPUT) ./plugin
```
## Configuration in Source Plugins
One peculiarity of plugins with event source capability is how they can accept user configurations. Other plugins can only be configured during the initialization phase through `plugin_init()`, whereas source plugins also take some parameters while opening the event stream with `plugin_open()`. This creates some ambiguity on **which** information should go inside the init configuration and what should be part of the open parameters instead.
One of the main differences between source and extractor plugins is the way they can accept user configurations. Extractor plugins can only be configured during the initialization phase through `plugin_init()`, whereas source plugins also accept and some parameters while opening the event stream with `plugin_open()`. This creates some ambiguity on **which** information should go inside the init configuration, and what should be part of the open parameters instead.
There's no silver bullet for this problem, and the solution strictly depends on the use cases of your plugin. However, there are some principles you can follow.
- The [init configuration](https://falco.org/docs/configuration/#plugins) should contain information that is used during the whole plugin lifecycle and that is used across both field extraction and event generation
- The init configuration is the right place for structured data. In fact, in most cases, plugins accept JSON strings as a configuration and also expose a schema describing/documenting the expected data format (see [`plugin_get_init_schema`](https://falco.org/docs/plugins/plugin-api-reference/#get-init-schema) for more details)
- Init configuration parameters should have the following annotations. See the [JSON Schema Validation specification](https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-basic-meta) for more details:
- `title`, which provides a short user-facing name for the parameter.
- `description`, which describes the parameter using a sentence or a short paragraph.
- `default` (optional), which provides the default value of the parameter.
- `required` (optional), which notes that the parameter value is required.
- `examples` (optional), which provides example values for the parameter.
- The init configuration is the right place for structured data. In fact, in most cases, plugins accept JSON strings as a configuration and also expose a schema describing/documenting the expected data format (see [`plugin_get_init_schema`](https://falco.org/docs/plugins/plugin-api-reference/#const-char-plugin-get-init-schema-ss-plugin-schema-type-schema-type-required-no) for more details)
- The open parameters should contain information that is only relevant for opening a specific event source, and their lifecycle ends at the invocation of `plugin_close()`
- The open parameters should contain minimal and non-structured information, such as a URI or a resource descriptor string. This is the reason why the framework does not support any schema definition for open parameters and treats them as an opaque string. Ideally, if more than one parameter is required to open a data source, comma-separated string concatenation is preferable to structured data formats such as JSON

View File

@ -1,35 +0,0 @@
# Plugin IDs (Sourcing Capability Only)
Using a unique `id` is mandatory to maintain interoperability across all plugins with _event sourcing_ capability. When a plugin is loaded by a compatible application (e.g., Falco), the `id` is used to route events to the correct plugin. Indeed, attempting to load two or more plugins using the same `id` will result in an error.
For this reason, The Falco Project maintains a [public registry of plugins](https://github.com/falcosecurity/plugins/blob/main/README.md#registering-a-new-plugin), which allows the assignment of a unique `id` for your plugin. However, some plugins may not be registered in the public registry. For example, if you are privately developing a plugin for your own use, you might use any `id` you want. To avoid conflicts in these situations, this document mandates general rules regarding `id` assignment and reservation.
## ID Blocks
The following ID ranges are designated for specific purposes:
| Block name | ID range | # of IDs | Description |
|---|---|---|---|
| Public | 01073741823 (30-bit) | 1073741824 | Used in the public registry. Single IDs in this range can be [assigned](#assigning-an-id) or [reserved](#reserving-an-id). |
| Private | 10737418242147483647 (30-bit) | 1073741824 | Used for private plugins (think of this range as the equivalent of 192.168.0.0/16 in networks). Organizations may use this range for plugins intended for their private domain. Interoperability is not guaranteed. |
| Reserved | 2147483648-3221225471 (30-bit) | 1073741824 | This range is reserved for future use and must not be used under any circumstances. |
| Internal | 3221225472-4294967295 (30-bit) | 1073741824 | This range is reserved for internal use and must not be used by plugins. It might be used by the plugin framework implementation for technical purposes. |
Notes:
- An `id` is a 32-bit unsigned integer. The MSBs are used to identify the block of IDs.
- Only IDs up to 1073741823 can be requested for use in the public registry.
- Only IDs up to 2147483647 can be used by plugins.
## Assigning an ID
The public registry is intended for assigning IDs to plugins that are publicly available. If you want to share your plugin with the community, you should follow the instructions reported in the [Registering a new plugin](../README.md#registering-a-new-plugin) section of this repository's documentation.
When making your request, please choose the next available ID in the [registry.yaml](../registry.yaml) file. The `id` will be definitively assigned to your plugin once the corresponding PR is merged, and the [registry.yaml](../registry.yaml) file is updated.
## Reserving an ID
For particular technical purposes or special cases, an `id` can be reserved so that it will not be assigned to any specific plugin. Notably, id 999 has been reserved for source plugin development. Any plugin author can temporarily use this `id`; however, it can't be assigned to any specific plugin and must not be used for purposes other than local development.
To reserve an `id`, you can use the same procedure for [registering a new plugin](../README.md#registering-a-new-plugin) and specify the `reserved: true` option.
Requests for `id` reservation will be evaluated on a case-by-case basis. The Falco Project reserves the right to reject any request for any reason.

View File

@ -1,40 +0,0 @@
### Registering a Plugin
Registering your plugin inside the registry helps ensure that some technical constraints are respected, such as that a [given ID is used by exactly one plugin with event source capability](https://falco.org/docs/concepts/plugins/architecture/#plugin-event-ids) and allows plugin authors to [coordinate about event source formats](https://falco.org/docs/concepts/plugins/architecture/#plugin-event-sources-and-interoperability). Moreover, this is a great way to share your plugin project with the community and engage with it, thus gaining new users and **increasing its visibility**. We encourage you to register your plugin in this registry before publishing it. You can add your plugins in this registry regardless of where its source code is hosted (there's a `url` field for this specifically).
The registration process involves adding an entry about your plugin inside the [registry.yaml](../registry.yaml) file by creating a Pull Request in this repository. Please be mindful of a few constraints that are automatically checked and required for your plugin to be accepted:
- The `name` field is mandatory and must be **unique** across all the plugins in the registry
- *(Sourcing Capability Only)* The `id` field is mandatory and must be **unique** in the registry across all the plugins with event source capability
- See [docs/plugin-ids.md](plugin-ids.md) for more information about plugin IDs
- The plugin `name` must match this [regular expression](https://en.wikipedia.org/wiki/Regular_expression): `^[a-z]+[a-z0-9-_\-]*$` (however, its not recommended to use `_` in the name, unless you are trying to match the name of a source or for particular reasons)
- The `source` *(Sourcing Capability Only)* and `sources` *(Extraction Capability Only)* must match this [regular expression](https://en.wikipedia.org/wiki/Regular_expression): `^[a-z]+[a-z0-9_]*$`
- The `url` field should point to the plugin source code
- The `rules_url` field should point to the default ruleset, if any
For reference, here's an example of an entry for a plugin with both event sourcing and field extraction capabilities:
```yaml
- name: k8saudit
description: ...
authors: ...
contact: ...
maintainers:
- name: The Falco Authors
email: cncf-falco-dev@lists.cncf.io
keywords:
- audit
- audit-log
- audit-events
- kubernetes
url: https://github.com/falcosecurity/plugins/tree/main/plugins/k8saudit
rules_url: https://github.com/falcosecurity/plugins/tree/main/plugins/k8saudit/rules
url: ...
license: ...
capabilities:
sourcing:
supported: true
id: 2
source: k8s_audit
extraction:
supported: true
```

View File

@ -1,6 +0,0 @@
*.so
*.a
*.o
.vscode
build*
libanomalydetection.so

View File

@ -1,38 +0,0 @@
# Changelog
## dev build (unreleased)
* [`406c517`](https://github.com/falcosecurity/plugins/commit/406c517) update(anomalydetection): tweak inits when count_min_sketch disabled + better...
* [`cb0fdb1`](https://github.com/falcosecurity/plugins/commit/cb0fdb1) update(anomalydetection): update documentation
* [`79c085e`](https://github.com/falcosecurity/plugins/commit/79c085e) update(anomalydetection): helper new filtercheck / output field anomaly.falco...
* [`489ef6d`](https://github.com/falcosecurity/plugins/commit/489ef6d) update(anomalydetection): ability to reset data structures w/ timers
* [`d4e72b8`](https://github.com/falcosecurity/plugins/commit/d4e72b8) update(anomalydetection): more usage safeguards and info log messages
* [`23bf05e`](https://github.com/falcosecurity/plugins/commit/23bf05e) update(anomalydetection): add some fallbacks / evt param extraction in cases ...
* [`1e8052c`](https://github.com/falcosecurity/plugins/commit/1e8052c) update(anomalydetection): add some custom behavior profile short-cut fields o...
* [`6a448c2`](https://github.com/falcosecurity/plugins/commit/6a448c2) update(anomalydetection): add MutexGuard (adopted from libs) to sketches data...
* [`f720e4a`](https://github.com/falcosecurity/plugins/commit/f720e4a) update(anomalydetection): finish currently supported behavior profile filterc...
* [`14a58d3`](https://github.com/falcosecurity/plugins/commit/14a58d3) update(anomalydetection): add lastevent_fd + enhance robustness / tests + sta...
* [`27747eb`](https://github.com/falcosecurity/plugins/commit/27747eb) update(anomalydetection): unit tests for proc lineage + add filterchecks 1/n
* [`fa2c05e`](https://github.com/falcosecurity/plugins/commit/fa2c05e) update(anomalydetection): populate info for proc args
* [`faf6636`](https://github.com/falcosecurity/plugins/commit/faf6636) update(anomalydetection): sync plugin to latest SDK changes
* [`6a834a3`](https://github.com/falcosecurity/plugins/commit/6a834a3) new(anomalydetection): init config + start behavior profile extraction
* [`73c11bd`](https://github.com/falcosecurity/plugins/commit/73c11bd) new(anomalydetection): start unit tests + bump libs and sdk
* [`f778922`](https://github.com/falcosecurity/plugins/commit/f778922) cleanup(anomalydetection): cms class updates
* [`6b89390`](https://github.com/falcosecurity/plugins/commit/6b89390) new(anomalydetection): init plugin / start dev

View File

@ -1,57 +0,0 @@
# 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.
#
cmake_minimum_required(VERSION 3.22)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
option(BUILD_TESTS "Enable tests" ON)
# Project metadata
project(
anomalydetection
VERSION 0.1.0
DESCRIPTION "Falco Anomaly Detection Plugin"
LANGUAGES CXX)
# Dependencies
include(FetchContent)
include(plugin-sdk-cpp)
include(libs) # Temporarily include libs for initial dev
include(xxhash)
# Project target
file(GLOB_RECURSE anomalydetection_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
add_library(anomalydetection SHARED ${anomalydetection_SOURCES} )
set_target_properties(anomalydetection PROPERTIES CXX_EXTENSIONS OFF)
# Project compilation options
target_compile_options(anomalydetection PRIVATE "-fPIC")
target_compile_options(anomalydetection PRIVATE "-Wl,-z,relro,-z,now")
target_compile_options(anomalydetection PRIVATE "-fstack-protector-strong")
# When compiling in Debug mode, this will define the DEBUG symbol for use in your code
target_compile_options(anomalydetection PUBLIC "$<$<CONFIG:DEBUG>:-DDEBUG>")
target_compile_features(anomalydetection PUBLIC cxx_std_17)
# Project includes
target_include_directories(
anomalydetection PRIVATE "${PLUGIN_SDK_INCLUDE}" "${XXHASH_INCLUDE}" "${LIBS_INCLUDE}")
# Project linked libraries
target_link_libraries(anomalydetection ${_REFLECTION})
# Testing
if(BUILD_TESTS)
add_subdirectory(test)
endif()

View File

@ -1,36 +0,0 @@
# 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.
#
NAME := anomalydetection
OUTPUT := lib$(NAME).so
all: $(OUTPUT)
clean:
rm -rf build $(OUTPUT)
# Temporarily include libs for initial dev
$(OUTPUT):
mkdir -p build \
&& cd build \
&& cmake \
-DCMAKE_BUILD_TYPE=Release \
-DMINIMAL_BUILD=ON \
-DUSE_BUNDLED_LIBELF=OFF \
-DCREATE_TEST_TARGETS=OFF \
../ \
&& make -j6 anomalydetection \
&& cp ./$(OUTPUT) ../$(OUTPUT)
readme:
@$(READMETOOL) -p ./$(OUTPUT) -f README.md

View File

@ -1,319 +0,0 @@
# Falcosecurity `anomalydetection` Plugin
**This plugin is experimental and under development**
This `anomalydetection` plugin has been created upon this [Proposal](https://github.com/falcosecurity/falco/blob/master/proposals/20230620-anomaly-detection-framework.md).
## Introduction
The `anomalydetection` plugin enhances {syscall} event analysis by incorporating anomaly detection estimates for probabilistic filtering.
### Functionality
The initial scope focuses exclusively on "CountMinSketch Powered Probabilistic Counting and Filtering" for a subset of syscalls and a selection of options for defining behavior profiles. This limitation is due to current restrictions related to the plugin API and SDK layout.
The new framework primarily aims to improve the usability of standard Falco rules. It may reduce the need for precise rule tuning, leverages probabilistic count estimates to auto-tune noisy rules on the fly, and enables the creation of broader Falco rules. Read more in the [Proposal](https://github.com/falcosecurity/falco/blob/master/proposals/20230620-anomaly-detection-framework.md).
### TL;DR
The official documentation will eventually be available on the Falco [Plugins](https://falco.org/docs/plugins/) site. Therefore, consider this README as not being a complete documentation for using this plugin.
*Disclaimer*: Anomaly detection can mean different things to different people. It's best to keep your expectations low for this plugin's current capabilities. For now, it is focused solely on probabilistic counting.
What this plugin is:
- **Initial step for real-time anomaly detection in Falco**: Introduces basic real-time anomaly detection methods on the host.
- **Probabilistic counting**: Currently supports only probabilistic counting, with the guarantee that any overcounting remains within an acceptable error margin.
- **Use-case dependent**: Requires careful derivation of custom use cases; no default use cases are provided at this time.
- **Limited by current API**: Subject to several restrictions due to plugin API and other limitations.
- **Built for future extensibility**: Designed to support more algorithms in the future, limited to those that can be implemented in a single data pass to ensure real-time performance.
- **Documentation is insufficient**: Expect to need hands-on exploration to understand usage and restrictions.
What this plugin is not:
- **Not a pre-trained AI/ML model**.
- **Not ready out-of-the-box**: No default configuration or use cases are provided at this time.
- **Not a universal solution**: Does not offer a one-size-fits-all approach to anomaly detection.
- **No multi-pass algorithms**: Algorithms requiring multiple data passes are not planned; the plugin is intended to remain real-time and efficient for applicable use cases.
- **Not yet battle-tested in production**.
### Outlook
In the near term, the plan is to expand the syscalls for which behavior profiles can be applied and to enhance the fields available for defining these profiles. The first version is quite restrictive in this regard due to current plugin API limitations. Additionally, from an algorithmic and capabilities point of view, we will explore the following:
- Support for HyperLogLog probabilistic distinct counting (ETA unknown).
- Overcoming the cold start problem by loading sketch data structures and counts from previous agent runs or from test environments (ETA unknown).
- Efficient and feasible options for real-time, single-pass time series analysis (ETA unknown).
### Plugin Official Name
`anomalydetection`
## Capabilities
The `anomalydetection` plugin implements 2 capabilities:
* `extraction`
* `parsing`
## Supported Fields
Here is the current set of output / filter fields introduced by this plugin:
<!-- README-PLUGIN-FIELDS -->
| NAME | TYPE | ARG | DESCRIPTION |
|------------------------------------|----------|-------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `anomaly.count_min_sketch` | `uint64` | Index | Count Min Sketch Estimate according to the specified behavior profile for a predefined set of {syscalls} events. Access different behavior profiles/sketches using indices. For instance, anomaly.count_min_sketch[0] retrieves the first behavior profile defined in the plugins' `init_config`. |
| `anomaly.count_min_sketch.profile` | `string` | Index | Concatenated string according to the specified behavior profile (not preserving original order). Access different behavior profiles using indices. For instance, anomaly.count_min_sketch.profile[0] retrieves the first behavior profile defined in the plugins' `init_config`. |
| `anomaly.falco.duration_ns` | `uint64` | None | Falco agent run duration in nanoseconds, which could be useful for ignoring some rare events at launch time while Falco is just starting to build up the counts in the sketch data structures (if applicable). |
<!-- /README-PLUGIN-FIELDS -->
## Usage
**Configuration**
Here's an example of configuration of `falco.yaml`:
```yaml
plugins:
- name: anomalydetection
library_path: libanomalydetection.so
init_config:
count_min_sketch:
enabled: true
n_sketches: 3
# `gamma_eps`: auto-calculate rows and cols; usage: [[gamma, eps], ...];
# gamma -> error probability -> determine d / rows / number of hash functions
# eps -> relative error -> determine w / cols / number of buckets
gamma_eps: [
[0.001, 0.0001],
[0.001, 0.0001],
[0.001, 0.0001]
]
# `rows_cols`: pass explicit dimensions, supersedes `gamma_eps`; usage: [[7, 27183], ...]; by default disabled when not used.
# rows_cols: []
behavior_profiles: [
{
"fields": "%container.id %custom.proc.aname.lineage.join[7] %custom.proc.aexepath.lineage.join[7] %proc.tty %proc.vpgid.name %proc.sname",
# execve, execveat exit event codes
"event_codes": [293, 331]
},
{
"fields": "%container.id %custom.proc.aname.lineage.join[7] %custom.proc.aexepath.lineage.join[7] %proc.tty %proc.vpgid.name %proc.sname %fd.name %fd.nameraw",
# open, openat, openat2 exit event codes
"event_codes": [3, 307, 327]
},
{
"fields": "%container.id %proc.cmdline",
# execve, execveat exit event codes
"event_codes": [293, 331],
# optional config `reset_timer_ms`, resets the data structure every x milliseconds, here one hour as example
# Remove JSON key if not wanted / needed.
"reset_timer_ms": 3600000
}
]
load_plugins: [anomalydetection]
```
The first version is quite restrictive with respect to the behavior profile's `event_codes` and `fields`. In a nutshell, you can currently define them only for a handful of event codes that Falco supports and a subset of the [Supported Fields for Conditions and Outputs](https://falco.org/docs/reference/rules/supported-fields/).
When you disable the `count_min_sketch` algorithm as shown below, all `anomaly.count_min_sketch` fields will be null.
```
count_min_sketch:
enabled: false
```
__NOTE__: Do not toggle the `enabled` key while hot reloading the config, as it currently does not get properly applied in such cases. Restart Falco with the `count_min_sketch` either enabled or disabled; subsequent reloads will work as expected.
**Behavior profiles for "execve/execveat/clone/clone3" events**
Example 1:
```
"event_codes": [293, 331],
```
Example 2:
```
"event_codes": [223, 335],
```
You can reference a behavior profile based on "execve/execveat/clone/clone3" events in any Falco rule that monitors any supported syscall. This works because every syscall is associated with a process.
**Behavior profiles for "fd-related" events**
Example 1:
```
rule: (evt.type in (open, openat, openat2) and evt.dir=<)
...
"event_codes": [3, 307, 327],
```
Example 2:
```
rule: (evt.type=connect and evt.dir=<)
...
"event_codes": [23],
```
You should avoid writing rules for arbitrary syscalls using "fd-related" behavior profiles because if a syscall doesn't involve a file descriptor (fd), referencing counts that rely on fd fields won't be meaningful.
Here's how it works:
- If your behavior profile includes `%fd.*` fields, all event codes in that profile must be related to file descriptors.
- If you use an "fd-related" behavior profile with a syscall that doesn't involve a file descriptor, the count will always be zero. While Falco won't crash, the anomaly detection estimate won't function as expected.
References:
- See the [Supported PPME `event codes`](#ppme-event-codes) reference below.
- See the [Supported Behavior Profiles `fields`](#behavior-profiles-fields) reference below.
**Open Parameters**:
This plugin does not have open params.
**Rules**
This plugin does not provide any default use cases or rules at the moment. More concrete use cases may be added at a later time.
Example of a dummy Falco rule using the `anomalydetection` fields for local testing:
```yaml
- macro: spawned_process
condition: (evt.type in (execve, execveat) and evt.dir=<)
- rule: execve count_min_sketch test
desc: "execve count_min_sketch test"
condition: spawned_process and proc.name=cat and anomaly.count_min_sketch[0] > 10
output: '%anomaly.count_min_sketch[0] %proc.pid %proc.ppid %proc.name %user.loginuid %user.name %user.uid %proc.cmdline %container.id %evt.type %evt.res %proc.cwd %proc.sid %proc.exepath %container.image.repository'
priority: NOTICE
tags: [maturity_sandbox, host, container, process, anomalydetection]
```
__NOTE__: Ensure you regularly execute `cat` commands. Once you have done so frequently enough, logs will start to appear. Alternatively, perform an inverse test to observe how quickly a very noisy rule gets silenced.
**Adoption**
To adopt the plugin framework, you can start by identifying rules in the [default](https://github.com/falcosecurity/rules) Falco ruleset that could benefit from auto-tuning based on your heuristics regarding counts. For example, you might broaden the scope of a rule and add an `anomaly.count_min_sketch` filter condition as a safety upper bound.
For initial adoption, we recommend creating new, separate rules inspired by existing upstream rules, rather than modifying rules that are already performing well in production.
Another approach is to duplicate a rule -- one version with and another without the anomaly detection filtering.
Alternatively, you can add the count estimates as output fields to provide additional forensic evidence without using the counts for on-host filtering.
Lastly, keep in mind that there is a configuration to reset the counts per behavior profile every x milliseconds if this suits your use case better.
### Running
This plugin requires Falco with version >= **0.38.2**.
1. Have Falco >= **0.38.2** installed and set up
2. Download the plugin's shared object (or build it yourself; see instructions below) and place it under `/usr/share/falco/plugins/libanomalydetection.so`
3. Modify the `falco.yaml` with the provided example [configuration](#configuration) above
4. Add a rule that uses `anomaly.count_min_sketch` as an output field and/or filter to `falco_rules.yaml`, and you're ready to go!
```shell
# Read the steps above before running Falco with this plugin
sudo falco -c falco.yaml -r falco_rules.yaml
```
## Local Development
### Build
```bash
git clone https://github.com/falcosecurity/plugins.git
cd plugins/plugins/anomalydetection
rm -f libanomalydetection.so;
rm -f build/libanomalydetection.so;
make;
# Copy the shared library to the expected location for `falco.yaml`, which is `library_path: libanomalydetection.so`
sudo mkdir -p /usr/share/falco/plugins/;
sudo cp -f libanomalydetection.so /usr/share/falco/plugins/libanomalydetection.so;
```
## References
### PPME event codes
Read this [blog post](https://falco.org/blog/adaptive-syscalls-selection/) to learn more about Falco's internal PPME event codes compared to the syscall names you are used to using in Falco rules.
The list below is complete, and no other event codes from Falco can be used for the behavior profiles at the moment. The binary will error out if used incorrectly. Thank you for your patience.
```CPP
typedef enum {
PPME_SYSCALL_OPEN_X = 3, // compare to "(evt.type=open and evt.dir=<)" in a Falco rule
PPME_SOCKET_CONNECT_X = 23, // compare to "(evt.type=connect and evt.dir=<)" in a Falco rule
PPME_SYSCALL_CREAT_X = 59, // compare to "(evt.type=creat and evt.dir=<)" in a Falco rule
PPME_SYSCALL_CLONE_20_X = 223, // compare to "(evt.type=clone and evt.dir=<)" in a Falco rule
PPME_SOCKET_ACCEPT_5_X = 247, // compare to "(evt.type=accept and evt.dir=<)" in a Falco rule
PPME_SYSCALL_EXECVE_19_X = 293, // compare to "(evt.type=execve and evt.dir=<)" in a Falco rule
PPME_SYSCALL_OPENAT_2_X = 307, // compare to "(evt.type=openat and evt.dir=<)" in a Falco rule
PPME_SYSCALL_OPENAT2_X = 327, // compare to "(evt.type=openat2 and evt.dir=<)" in a Falco rule
PPME_SYSCALL_EXECVEAT_X = 331, // compare to "(evt.type=execveat and evt.dir=<)" in a Falco rule
PPME_SYSCALL_CLONE3_X = 335, // compare to "(evt.type=clone3 and evt.dir=<)" in a Falco rule
PPME_SYSCALL_OPEN_BY_HANDLE_AT_X = 337, // compare to "(evt.type=open_by_handle_at and evt.dir=<)" in a Falco rule
PPME_SOCKET_ACCEPT4_6_X = 389, // compare to "(evt.type=accept4 and evt.dir=<)" in a Falco rule
} ppm_event_code;
```
### Behavior Profiles fields
Compare to [Supported Fields for Conditions and Outputs](https://falco.org/docs/reference/rules/supported-fields/).
The list below is complete, and no other fields from Falco can be used for the behavior profiles at the moment. The binary will error out if used incorrectly. Thank you for your patience.
| Supported Behavior Profile Field | Description |
| --- | --- |
|proc.exe|The first command-line argument (i.e., argv[0]), typically the executable name or a custom string as specified by the user. It is primarily obtained from syscall arguments, truncated after 4096 bytes, or, as a fallback, by reading /proc/PID/cmdline, in which case it may be truncated after 1024 bytes. This field may differ from the last component of proc.exepath, reflecting how command invocation and execution paths can vary.|
|proc.pexe|The proc.exe (first command line argument argv[0]) of the parent process.|
|proc.aexe|The proc.exe (first command line argument argv[0]) for a specific process ancestor. You can access different levels of ancestors by using indices. For example, proc.aexe[1] retrieves the proc.exe of the parent process, proc.aexe[2] retrieves the proc.exe of the grandparent process, and so on. The current process's proc.exe line can be obtained using proc.aexe[0]. When used without any arguments, proc.aexe is applicable only in filters and matches any of the process ancestors. For instance, you can use `proc.aexe endswith java` to match any process ancestor whose proc.exe ends with the term `java`.|
|proc.exepath|The full executable path of a process, resolving to the canonical path for symlinks. This is primarily obtained from the kernel, or as a fallback, by reading /proc/PID/exe (in the latter case, the path is truncated after 1024 bytes). For eBPF drivers, due to verifier limits, path components may be truncated to 24 for legacy eBPF on kernel <5.2, 48 for legacy eBPF on kernel >=5.2, or 96 for modern eBPF.|
|proc.pexepath|The proc.exepath (full executable path) of the parent process.|
|proc.aexepath|The proc.exepath (full executable path) for a specific process ancestor. You can access different levels of ancestors by using indices. For example, proc.aexepath[1] retrieves the proc.exepath of the parent process, proc.aexepath[2] retrieves the proc.exepath of the grandparent process, and so on. The current process's proc.exepath line can be obtained using proc.aexepath[0]. When used without any arguments, proc.aexepath is applicable only in filters and matches any of the process ancestors. For instance, you can use `proc.aexepath endswith java` to match any process ancestor whose path ends with the term `java`.|
|proc.name|The process name (truncated after 16 characters) generating the event (task->comm). Truncation is determined by kernel settings and not by Falco. This field is collected from the syscalls args or, as a fallback, extracted from /proc/PID/status. The name of the process and the name of the executable file on disk (if applicable) can be different if a process is given a custom name which is often the case for example for java applications.|
|proc.pname|The proc.name truncated after 16 characters) of the process generating the event.|
|proc.aname|The proc.name (truncated after 16 characters) for a specific process ancestor. You can access different levels of ancestors by using indices. For example, proc.aname[1] retrieves the proc.name of the parent process, proc.aname[2] retrieves the proc.name of the grandparent process, and so on. The current process's proc.name line can be obtained using proc.aname[0]. When used without any arguments, proc.aname is applicable only in filters and matches any of the process ancestors. For instance, you can use `proc.aname=bash` to match any process ancestor whose name is `bash`.|
|proc.args|The arguments passed on the command line when starting the process generating the event excluding argv[0] (truncated after 4096 bytes). This field is collected from the syscalls args or, as a fallback, extracted from /proc/PID/cmdline.|
|proc.cmdline|The concatenation of `proc.name + proc.args` (truncated after 4096 bytes) when starting the process generating the event.|
|proc.pcmdline|The proc.cmdline (full command line (proc.name + proc.args)) of the parent of the process generating the event.|
|proc.acmdline|The full command line (proc.name + proc.args) for a specific process ancestor. You can access different levels of ancestors by using indices. For example, proc.acmdline[1] retrieves the full command line of the parent process, proc.acmdline[2] retrieves the proc.cmdline of the grandparent process, and so on. The current process's full command line can be obtained using proc.acmdline[0]. When used without any arguments, proc.acmdline is applicable only in filters and matches any of the process ancestors. For instance, you can use `proc.acmdline contains base64` to match any process ancestor whose command line contains the term base64.|
|proc.cmdnargs|The number of command line args (proc.args).|
|proc.cmdlenargs|The total count of characters / length of the command line args (proc.args) combined excluding whitespaces between args.|
|proc.exeline|The full command line, with exe as first argument (proc.exe + proc.args) when starting the process generating the event.|
|proc.env|The environment variables of the process generating the event as concatenated string 'ENV_NAME=value ENV_NAME1=value1'. Can also be used to extract the value of a known env variable, e.g. proc.env[ENV_NAME].|
|proc.cwd|The current working directory of the event.|
|proc.tty|The controlling terminal of the process. 0 for processes without a terminal.|
|proc.pid|The id of the process generating the event.|
|proc.ppid|The pid of the parent of the process generating the event.|
|proc.apid|The pid for a specific process ancestor. You can access different levels of ancestors by using indices. For example, proc.apid[1] retrieves the pid of the parent process, proc.apid[2] retrieves the pid of the grandparent process, and so on. The current process's pid can be obtained using proc.apid[0]. When used without any arguments, proc.apid is applicable only in filters and matches any of the process ancestors. For instance, you can use `proc.apid=1337` to match any process ancestor whose pid is equal to 1337.|
|proc.vpid|The id of the process generating the event as seen from its current PID namespace.|
|proc.pvpid|The id of the parent process generating the event as seen from its current PID namespace.|
|proc.sid|The session id of the process generating the event.|
|proc.sname|The name of the current process's session leader. This is either the process with pid=proc.sid or the eldest ancestor that has the same sid as the current process.|
|proc.sid.exe|The first command line argument argv[0] (usually the executable name or a custom one) of the current process's session leader. This is either the process with pid=proc.sid or the eldest ancestor that has the same sid as the current process.|
|proc.sid.exepath|The full executable path of the current process's session leader. This is either the process with pid=proc.sid or the eldest ancestor that has the same sid as the current process.|
|proc.vpgid|The process group id of the process generating the event, as seen from its current PID namespace.|
|proc.vpgid.name|The name of the current process's process group leader. This is either the process with proc.vpgid == proc.vpid or the eldest ancestor that has the same vpgid as the current process. The description of `proc.is_vpgid_leader` offers additional insights.|
|proc.vpgid.exe|The first command line argument argv[0] (usually the executable name or a custom one) of the current process's process group leader. This is either the process with proc.vpgid == proc.vpid or the eldest ancestor that has the same vpgid as the current process. The description of `proc.is_vpgid_leader` offers additional insights.|
|proc.vpgid.exepath|The full executable path of the current process's process group leader. This is either the process with proc.vpgid == proc.vpid or the eldest ancestor that has the same vpgid as the current process. The description of `proc.is_vpgid_leader` offers additional insights.|
|proc.is_exe_writable|'true' if this process' executable file is writable by the same user that spawned the process.|
|proc.is_exe_upper_layer|'true' if this process' executable file is in upper layer in overlayfs. This field value can only be trusted if the underlying kernel version is greater or equal than 3.18.0, since overlayfs was introduced at that time.|
|proc.is_exe_from_memfd|'true' if the executable file of the current process is an anonymous file created using memfd_create() and is being executed by referencing its file descriptor (fd). This type of file exists only in memory and not on disk. Relevant to detect malicious in-memory code injection. Requires kernel version greater or equal to 3.17.0.|
|proc.is_sid_leader|'true' if this process is the leader of the process session, proc.sid == proc.vpid. For host processes vpid reflects pid.|
|proc.is_vpgid_leader|'true' if this process is the leader of the virtual process group, proc.vpgid == proc.vpid. For host processes vpgid and vpid reflect pgid and pid. Can help to distinguish if the process was 'directly' executed for instance in a tty (similar to bash history logging, `is_vpgid_leader` would be 'true') or executed as descendent process in the same process group which for example is the case when subprocesses are spawned from a script (`is_vpgid_leader` would be 'false').|
|proc.exe_ino|The inode number of the executable file on disk. Can be correlated with fd.ino.|
|proc.exe_ino.ctime|Last status change time of executable file (inode->ctime) as epoch timestamp in nanoseconds. Time is changed by writing or by setting inode information e.g. owner, group, link count, mode etc.|
|proc.exe_ino.mtime|Last modification time of executable file (inode->mtime) as epoch timestamp in nanoseconds. Time is changed by file modifications, e.g. by mknod, truncate, utime, write of more than zero bytes etc. For tracking changes in owner, group, link count or mode, use proc.exe_ino.ctime instead.|
|container.id|The truncated container ID (first 12 characters), e.g. 3ad7b26ded6d is extracted from the Linux cgroups by Falco within the kernel. Consequently, this field is reliably available and serves as the lookup key for Falco's synchronous or asynchronous requests against the container runtime socket to retrieve all other `'container.*'` information. One important aspect to be aware of is that if the process occurs on the host, meaning not in the container PID namespace, this field is set to a string called 'host'. In Kubernetes, pod sandbox container processes can exist where `container.id` matches `k8s.pod.sandbox_id`, lacking other 'container.*' details.|
|fd.num|the unique number identifying the file descriptor.|
|fd.name|FD full name. If the fd is a file, this field contains the full path. If the FD is a socket, this field contain the connection tuple.|
|fd.directory|If the fd is a file, the directory that contains it.|
|fd.filename|If the fd is a file, the filename without the path.|
|fd.dev|device number (major/minor) containing the referenced file|
|fd.ino|inode number of the referenced file|
|fd.nameraw|FD full name raw. Just like fd.name, but only used if fd is a file path. File path is kept raw with limited sanitization and without deriving the absolute path.|
|custom.proc.aname.lineage.join|[Incubating] String concatenate the process lineage to achieve better performance. It requires an argument to specify the maximum level of traversal, e.g. 'custom.proc.aname.lineage.join[7]'. This is a custom plugin specific field for the anomaly behavior profiles only. It may be dperecated in the future.|
|custom.proc.aexe.lineage.join|[Incubating] String concatenate the process lineage to achieve better performance. It requires an argument to specify the maximum level of traversal, e.g. 'custom.proc.aexe.lineage.join[7]'. This is a custom plugin specific field for the anomaly behavior profiles only. It may be dperecated in the future.|
|custom.proc.aexepath.lineage.join|[Incubating] String concatenate the process lineage to achieve better performance. It requires an argument to specify the maximum level of traversal, e.g. 'custom.proc.aexepath.lineage.join[7]'. This is a custom plugin specific field for the anomaly behavior profiles only. It may be dperecated in the future.|
|custom.fd.name.part1|[Incubating] For fd related network events only. Part 1 as string of the ip tuple in the format 'ip:port', e.g '172.40.111.222:54321' given fd.name '172.40.111.222:54321->142.251.111.147:443'. It may be dperecated in the future.|
|custom.fd.name.part2|[Incubating] For fd related network events only. Part 2 as string of the ip tuple in the format 'ip:port', e.g.'142.251.111.147:443' given fd.name '172.40.111.222:54321->142.251.111.147:443'. This is a custom plugin specific field for the anomaly behavior profiles only. It may be dperecated in the future.|

View File

@ -1,26 +0,0 @@
# 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.
#
message(STATUS "Fetching libs at 'https://github.com/falcosecurity/libs.git'")
FetchContent_Declare(
libs
GIT_REPOSITORY https://github.com/falcosecurity/libs.git
GIT_TAG 273299c5832ab7efa6a93547f7c3bd55706b135c
CONFIGURE_COMMAND "" BUILD_COMMAND "")
FetchContent_MakeAvailable(libs)
set(LIBS_INCLUDE "${libs_SOURCE_DIR}")
set(LIBS_DIR "${libs_SOURCE_DIR}")
message(STATUS "Using libs include at '${LIBS_INCLUDE}'")

View File

@ -1,27 +0,0 @@
# 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.
#
message(
STATUS
"Fetching plugin-sdk-cpp at 'https://github.com/falcosecurity/plugin-sdk-cpp.git'"
)
FetchContent_Declare(
plugin-sdk-cpp
GIT_REPOSITORY https://github.com/falcosecurity/plugin-sdk-cpp.git
GIT_TAG 1c46ba02e8e9fe30a8362a54e99a6c3c804661f6)
FetchContent_MakeAvailable(plugin-sdk-cpp)
set(PLUGIN_SDK_INCLUDE "${plugin-sdk-cpp_SOURCE_DIR}/include")
message(STATUS "Using plugin-sdk-cpp include at '${PLUGIN_SDK_INCLUDE}'")

View File

@ -1,31 +0,0 @@
# 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.
#
message(
STATUS
"Fetching xxhash at 'https://raw.githubusercontent.com/Cyan4973/xxHash/v0.8.2/xxhash.h'"
)
FetchContent_Declare(
# BSD 2-Clause License
xxhash
URL "https://raw.githubusercontent.com/Cyan4973/xxHash/v0.8.2/xxhash.h"
URL_HASH SHA256=be275e9db21a503c37f24683cdb4908f2370a3e35ab96e02c4ea73dc8e399c43
DOWNLOAD_NAME "xxhash.h"
DOWNLOAD_NO_EXTRACT TRUE
)
FetchContent_MakeAvailable(xxhash)
set(XXHASH_INCLUDE "${xxhash_SOURCE_DIR}")
message(STATUS "Using xxhash include at '${XXHASH_INCLUDE}'")

View File

@ -1,228 +0,0 @@
// 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.
*/
#pragma once
#include "xxhash_ext.h"
#include <iostream>
#include <cstdint>
#include <cmath>
#include <vector>
#include <algorithm>
#include <memory>
/*
CountMinSketch Powered Probabilistic Counting and Filtering
Falco Proposal: https://github.com/falcosecurity/falco/blob/master/proposals/20230620-anomaly-detection-framework.md
*/
namespace plugin::anomalydetection::num
{
template<typename T>
class cms
{
private:
std::unique_ptr<std::unique_ptr<T[]>[]> sketch;
uint64_t d_; // d / Rows / number of hash functions
uint64_t w_; // w / Cols / number of buckets
double gamma_; // Error probability (e.g. 0.001)
double eps_; // Relative error (e.g. 0.0001)
public:
static uint64_t calculate_d_rows_from_gamma(double gamma)
{
// -> determine Rows / number of hash functions
return static_cast<uint64_t>(std::ceil(std::log(1.0 / gamma)));
}
static double calculate_gamma_rows_from_d(uint64_t d)
{
// -> reverse calculate error probability from Rows / number of hash functions
return 1.0 / std::exp(d);
}
static uint64_t calculate_w_cols_buckets_from_eps(double eps)
{
// -> determine Cols / number of buckets
return static_cast<uint64_t>(std::ceil(std::exp(1) / eps));
}
static double calculate_eps_cols_buckets_from_w(uint64_t w)
{
// -> reverse calculate relative error from Cols / number of buckets
return std::exp(1) / w;
}
cms(double gamma, double eps)
{
d_ = calculate_d_rows_from_gamma(gamma); // -> determine Rows / number of hash functions
w_ = calculate_w_cols_buckets_from_eps(eps); // -> determine Cols / number of buckets
gamma_ = gamma;
eps_ = eps;
sketch = std::make_unique<std::unique_ptr<T[]>[]>(d_);
for (uint64_t i = 0; i < d_; ++i)
{
sketch[i] = std::make_unique<T[]>(w_);
std::fill(sketch[i].get(), sketch[i].get() + w_, static_cast<T>(0)); // Init to 0
}
}
// Overloaded constructor
cms(uint64_t d, uint64_t w)
{
d_ = d;
w_ = w;
gamma_ = calculate_gamma_rows_from_d(d); // -> reverse calculate error probability from Rows / number of hash functions
eps_ = calculate_eps_cols_buckets_from_w(w); // -> reverse calculate relative error from Cols / number of buckets
sketch = std::make_unique<std::unique_ptr<T[]>[]>(d_);
for (uint64_t i = 0; i < d_; ++i)
{
sketch[i] = std::make_unique<T[]>(w_);
std::fill(sketch[i].get(), sketch[i].get() + w_, static_cast<T>(0)); // Init to 0
}
}
void reset()
{
// Reset data structure
for (uint64_t i = 0; i < d_; ++i)
{
std::fill(sketch[i].get(), sketch[i].get() + w_, static_cast<T>(0));
}
}
uint64_t hash_XXH3_seed(std::string value, uint64_t seed) const
{
// using https://raw.githubusercontent.com/Cyan4973/xxHash/v0.8.2/xxhash.h
// Requirement: Need fast and reliable independent hash functions.
uint64_t hash = XXH3_64bits_withSeed(value.c_str(), value.size(), seed);
return hash;
}
void update(std::string value, T count)
{
if (value.empty())
{
return;
}
// Update counts for each hash function.
// Note: d is typically very small (e.g. < 10)
for (uint64_t seed = 0; seed < d_; ++seed)
{
// Map the hash value to an index of the current sketch Row by taking the modulo of the hash value, where w is the number of buckets.
// Simply loop over d, which is the number of hash functions, to obtain a seed in order to use independent hash functions for each Row.
sketch[seed][hash_XXH3_seed(value, seed) % w_] += count;
}
}
T update_estimate(std::string value, T count) const
{
if (value.empty())
{
return T();
}
std::vector<T> estimates;
// Same as the update function, but also returns the minimum count as an estimate.
// Note: d is typically very small (e.g. < 10)
for (uint64_t seed = 0; seed < d_; ++seed)
{
uint64_t index = hash_XXH3_seed(value, seed) % w_;
sketch[seed][index] += count;
estimates.push_back(sketch[seed][index]);
}
auto min_element = std::min_element(estimates.begin(), estimates.end());
return min_element != estimates.end() ? *min_element : T();
}
T estimate(std::string value) const
{
if (value.empty())
{
return T();
}
std::vector<T> estimates;
// Return the minimum count across hash functions as an estimate.
// Note: d is typically very small (e.g. < 10)
for (uint64_t seed = 0; seed < d_; ++seed)
{
uint64_t index = hash_XXH3_seed(value, seed) % w_;
estimates.push_back(sketch[seed][index]);
}
auto min_element = std::min_element(estimates.begin(), estimates.end());
return min_element != estimates.end() ? *min_element : T();
}
T get_item(uint64_t row, uint64_t col) const
{
if (row >= 0 && row < d_ && col >= 0 && col < w_)
{
return sketch[row][col];
} else
{
return T();
}
}
size_t get_size_bytes() const
{
return d_ * w_ * sizeof(T);
}
static size_t get_size_bytes(uint64_t d, uint64_t w)
{
return d * w * sizeof(T);
}
std::pair<uint64_t, uint64_t> get_dimensions() const
{
return std::make_pair(d_, w_);
}
// Return Rows / number of hash functions
uint64_t get_d() const
{
return d_;
}
// Return Cols / number of buckets
uint64_t get_w() const
{
return w_;
}
// Return error probability
double get_gamma() const
{
return gamma_;
}
// Return relative error
double get_eps() const
{
return eps_;
}
cms(cms&&) noexcept = default;
cms(const cms&) = default;
cms& operator=(cms&&) noexcept = default;
cms& operator=(const cms&) = default;
cms() = delete;
};
} // namespace plugin::anomalydetection::num

View File

@ -1,19 +0,0 @@
// 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.
*/
#define XXH_INLINE_ALL
#include <xxhash.h>

File diff suppressed because it is too large Load Diff

View File

@ -1,228 +0,0 @@
// 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.
*/
#pragma once
#include "num/cms.h"
#include "plugin_consts.h"
#include "plugin_utils.h"
#include "plugin_mutex.h"
#include "plugin_thread_manager.h"
#include "plugin_sinsp_filterchecks.h"
#include <falcosecurity/sdk.h>
#include <driver/ppm_events_public.h> // Temporary workaround to avoid redefining syscalls PPME events and risking being out of sync
#include <thread>
#include <atomic>
#include <chrono>
#include <unordered_map>
#include <unordered_set>
#include <sstream>
#define UINT32_MAX (4294967295U)
#define PPM_AT_FDCWD -100
#define SECOND_TO_NS 1000000000ULL
struct sinsp_param
{
uint16_t param_len;
uint8_t* param_pointer;
};
class anomalydetection
{
public:
anomalydetection() : m_thread_manager() {}
// Keep this aligned with `get_fields`
enum anomalydetection_fields
{
ANOMALYDETECTION_COUNT_MIN_SKETCH_COUNT = 0,
ANOMALYDETECTION_COUNT_MIN_SKETCH_BEHAVIOR_PROFILE_CONCAT_STR,
ANOMALYDETECTION_FALCO_DURATION_NS,
ANOMALYDETECTION_FIELD_MAX
};
//////////////////////////
// General plugin API
//////////////////////////
virtual ~anomalydetection() = default;
std::string get_name() { return PLUGIN_NAME; }
std::string get_version() { return PLUGIN_VERSION; }
std::string get_description() { return PLUGIN_DESCRIPTION; }
std::string get_contact() { return PLUGIN_CONTACT; }
std::string get_required_api_version()
{
return PLUGIN_REQUIRED_API_VERSION;
}
falcosecurity::init_schema get_init_schema();
void parse_init_config(nlohmann::json& config_json);
bool init(falcosecurity::init_input& in);
// todo
// void destroy();
std::string get_last_error() { return m_lasterr; }
static void log_error(std::string err_mess);
//////////////////////////
// Extract capability
//////////////////////////
// required; standard plugin API
std::vector<std::string> get_extract_event_sources()
{
return {"syscall"};
}
// required; standard plugin API
std::vector<falcosecurity::field_info> get_fields();
// required; standard plugin API
bool extract(const falcosecurity::extract_fields_input& in);
//////////////////////////
// Parse capability
//////////////////////////
// required; standard plugin API
std::vector<std::string> get_parse_event_sources()
{
return {"syscall"};
}
// required; standard plugin API
std::vector<falcosecurity::event_type> get_parse_event_types()
{
std::vector<falcosecurity::event_type> event_types;
// Temporary workaround
for (int i = PPME_GENERIC_E; i <= PPM_EVENT_MAX; ++i)
{
event_types.push_back(static_cast<falcosecurity::event_type>(i));
}
return event_types;
}
// required; standard plugin API
bool parse_event(const falcosecurity::parse_event_input& in);
// Custom helper functions within event parsing
bool extract_filterchecks_concat_profile(const falcosecurity::event_reader &evt, const falcosecurity::table_reader &tr, const std::vector<plugin_sinsp_filterchecks_field>& fields, std::string& behavior_profile_concat_str);
std::string extract_filterchecks_evt_params_fallbacks(const falcosecurity::event_reader &evt, const plugin_sinsp_filterchecks_field& field, const std::string& cwd = "");
private:
// Manages plugin side threads, such as resetting the count min sketch data structures
ThreadManager m_thread_manager;
// Epoch of Falco agent run start, re-creates libs agent_info->start_ts_epoch info
uint64_t m_falco_start_ts_epoch_ns;
/* Note: While we have set the stage for supporting multiple algorithms in this plugin,
the class is currently designed with direct members specific to the count_min_sketch use case.
This will be refactored and refined in the future.
*/
bool m_count_min_sketch_enabled = false;
uint32_t m_n_sketches = 0;
std::vector<std::vector<double>> m_gamma_eps;
std::vector<std::vector<uint64_t>> m_rows_cols; // If set supersedes m_gamma_eps
std::vector<std::vector<plugin_sinsp_filterchecks_field>> m_behavior_profiles_fields;
std::vector<std::unordered_set<ppm_event_code>> m_behavior_profiles_event_codes;
std::vector<uint64_t> m_reset_timers;
// Plugin managed state table specific to the count_min_sketch use case
plugin_anomalydetection::Mutex<std::vector<std::shared_ptr<plugin::anomalydetection::num::cms<uint64_t>>>> m_count_min_sketches;
// required; standard plugin API
std::string m_lasterr;
// required; standard plugin API; accessor to falcosecurity/libs' thread table
falcosecurity::table m_thread_table;
/* Subtables */
falcosecurity::table_field m_args; ///< args subtable
falcosecurity::table_field m_env; ///< env variables subtable
falcosecurity::table_field m_fds; ///< fd subtable
/* proc related */
falcosecurity::table_field m_tid; ///< The id of this thread
falcosecurity::table_field m_pid; ///< The id of the process containing this thread. In single thread threads, this is equal to tid.
falcosecurity::table_field m_ptid; ///< The id of the process that started this thread.
falcosecurity::table_field m_sid; ///< The session id of the process containing this thread.
falcosecurity::table_field m_comm; ///< Command name (e.g. "top")
falcosecurity::table_field m_exe; ///< argv[0] (e.g. "sshd: user@pts/4")
falcosecurity::table_field m_exepath; ///< full executable path
falcosecurity::table_field m_exe_writable;
falcosecurity::table_field m_exe_upper_layer; ///< True if the executable file belongs to upper layer in overlayfs
falcosecurity::table_field m_exe_from_memfd; ///< True if the executable is stored in fileless memory referenced by memfd
falcosecurity::table_field m_exe_ino;
falcosecurity::table_field m_exe_ino_ctime;
falcosecurity::table_field m_exe_ino_mtime;
// falcosecurity::table_field m_cap_permitted; // todo fix/expose via plugin API
// falcosecurity::table_field m_cap_inheritable; // todo fix/expose via plugin API
// falcosecurity::table_field m_cap_effective; // todo fix/expose via plugin API
falcosecurity::table_field m_args_value; ///< Value entry to command line arguments (e.g. "-d1") from the args array
falcosecurity::table_field m_env_value; ///< Value entry
falcosecurity::table_field m_group; ///< group infos
falcosecurity::table_field m_vtid; ///< The virtual id of this thread.
falcosecurity::table_field m_vpid; ///< The virtual id of the process containing this thread. In single thread threads, this is equal to vtid.
falcosecurity::table_field m_vpgid; // The virtual process group id, as seen from its pid namespace
falcosecurity::table_field m_tty; ///< Number of controlling terminal
falcosecurity::table_field m_cwd; ///< current working directory
/* user related */
// Not available until the next libs plugins API expansion
// falcosecurity::table_field m_uid; ///< user uid
// falcosecurity::table_field m_user; ///< user infos
// falcosecurity::table_field m_loginuid; ///< auid
// falcosecurity::table_field m_loginuser; ///< loginuser infos (auid)
/* fd related */
// falcosecurity::table_field m_fd_type_value; // todo fix/expose via plugin API
falcosecurity::table_field m_fd_openflags_value;
// falcosecurity::table_field m_fd_sockinfo_value; // todo fix/expose via plugin API
falcosecurity::table_field m_fd_name_value;
falcosecurity::table_field m_fd_nameraw_value;
falcosecurity::table_field m_fd_oldname_value;
falcosecurity::table_field m_fd_flags_value;
falcosecurity::table_field m_fd_dev_value;
falcosecurity::table_field m_fd_mount_id_value;
falcosecurity::table_field m_fd_ino_value;
falcosecurity::table_field m_fd_pid_value;
// falcosecurity::table_field m_fd_fd_value; // todo fix/expose via plugin API
/* container related */
falcosecurity::table_field m_container_id; ///< heuristic-based container id
/* Custom write/read fields*/
falcosecurity::table_field m_lastevent_fd_field; // todo fix/expose via plugin API
};
// required; standard plugin API
FALCOSECURITY_PLUGIN(anomalydetection);
FALCOSECURITY_PLUGIN_FIELD_EXTRACTION(anomalydetection);
FALCOSECURITY_PLUGIN_EVENT_PARSING(anomalydetection);

Some files were not shown because too many files have changed in this diff Show More