Skip to content

Add job for ESCU Tests #358

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 136 additions & 1 deletion .github/workflows/reusable-build-test-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ jobs:
runs-on: ubuntu-latest
outputs:
execute-knowledge-labeled: ${{ steps.configure-tests-on-labels.outputs.execute_knowledge_labeled }}
execute-escu-labeled: ${{ steps.configure-tests-on-labels.outputs.execute_escu_labeled }}
execute-spl2-labeled: ${{ steps.configure-tests-on-labels.outputs.execute_spl2_labeled }}
execute-ui-labeled: ${{ steps.configure-tests-on-labels.outputs.execute_ui_labeled }}
execute-modinput-labeled: ${{ steps.configure-tests-on-labels.outputs.execute_modinput_functional_labeled }}
Expand Down Expand Up @@ -161,7 +162,7 @@ jobs:
run: |
set +e
declare -A EXECUTE_LABELED
TESTSET=("execute_knowledge" "execute_spl2" "execute_ui" "execute_modinput_functional" "execute_ucc_modinput_functional" "execute_scripted_inputs" "execute_upgrade")
TESTSET=("execute_knowledge" "execute_escu" "execute_spl2" "execute_ui" "execute_modinput_functional" "execute_ucc_modinput_functional" "execute_scripted_inputs" "execute_upgrade")
for test_type in "${TESTSET[@]}"; do
EXECUTE_LABELED["$test_type"]="false"
done
Expand Down Expand Up @@ -399,6 +400,140 @@ jobs:
find tests -type d -maxdepth 1 -mindepth 1 | sed 's|^tests/||g' | while read -r TESTSET; do echo "$TESTSET=true" >> "$GITHUB_OUTPUT"; echo "$TESTSET::true"; done
find package/default/data -type d -name "spl2" -maxdepth 1 -mindepth 1 | sed 's|^package/default/data/||g' | while read -r TESTSET; do echo "$TESTSET=true" >> "$GITHUB_OUTPUT"; echo "$TESTSET::true"; done

run-escu-tests:
if: ${{ !cancelled() && needs.setup-workflow.outputs.execute-escu-labeled == 'true' }}
needs:
- build
- setup-workflow
- setup
runs-on: large-ubuntu-22.04-32core
permissions:
actions: read
deployments: read
contents: read
packages: read
statuses: read
checks: write
steps:
- name: Checkout TA
uses: actions/checkout@v4

- name: Checkout Security Content
uses: actions/checkout@v4
with:
repository: splunk/security_content
path: security_content
ref: refs/heads/develop

- uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install contentctl pyyaml

- name: Download TA Build Artifact
uses: actions/download-artifact@v4
with:
name: package-splunkbase
path: ta_build

- name: Get the build path
run: |
TA_BUILD=$(ls ta_build)
TA_BUILD_PATH="${{ github.workspace }}/ta_build/$TA_BUILD"
echo "TA_BUILD_PATH=$TA_BUILD_PATH" >> $GITHUB_ENV

- name: Filter ESCU Detections and swap TA to actual build
id: filter-detection-files
shell: python
run: |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move it out to a separate file / script?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I know what would be the appropriate place to put that file?

import yaml
import os
import configparser
import re

GITHUB_REPOSITORY = os.environ.get("GITHUB_REPOSITORY", "")

# Parse app.conf get the appid of the TA.
config = configparser.ConfigParser(strict=False)
config.read("package/default/app.conf")
APP_ID = config.get("id", "name")
APP_LABEL = config.get("ui", "label")

print(f"APP_ID = {APP_ID}, APP_LABEL = {APP_LABEL}")

# Read the file and remove trailing backslashes
with open("package/default/props.conf", "r") as f:
content = f.read()

# Remove trailing backslashes followed by a newline
updated_content = re.sub(r"\\\n", "", content)

# Write the cleaned content to a new file
with open("package/default/props.conf", "w") as f:
f.write(updated_content)

# Parse props.conf and collect all the sourcetypes in a list.
config = configparser.ConfigParser(strict=False)
config.read("package/default/props.conf")
sourcetypes = config.sections()

# Load the YAML content
with open("security_content/contentctl.yml", "r") as file:
data = yaml.safe_load(file)

app_found = False
for app in data["apps"]:
if app['appid'] == APP_ID or APP_ID in app['hardcoded_path'] or GITHUB_REPOSITORY in app['hardcoded_path'] or app["title"] == APP_LABEL or (app['appid'] == "PALO_ALTO_NETWORKS_ADD_ON_FOR_SPLUNK" and APP_ID == "Splunk_TA_paloalto_networks"):
app['hardcoded_path'] = "${{ env.TA_BUILD_PATH }}"
app_found = True

if not app_found:
print(f"App not found in contentctl.yml file. Exiting.")
exit(127)


# Write the modified data to the contentctl.yml file
with open("security_content/contentctl.yml", "w") as file:
yaml.dump(data,file,sort_keys=False)

# Filter out the detections based on the collected sourcetypes
base_dir = "security_content/detections"
detection_files = ""

for root, dirs, files in os.walk(base_dir):
for file in files:
file_path = os.path.join(root, file)
try:
with open(file_path, "r") as yaml_file:
file_content = yaml.safe_load(yaml_file)
if "deprecated" not in file_path and (file_content["tests"][0]["attack_data"][0]["sourcetype"] in sourcetypes or file_content["tests"][0]["attack_data"][0]["source"] in sourcetypes):
detection_files += file_path.replace("security_content/", "") + " "
except Exception as e:
continue

# Save detection_files as an output variable
with open(os.getenv('GITHUB_OUTPUT'), 'w') as output_file:
output_file.write(f"DETECTION_FILES={detection_files}")

print(f"Filtered Detection files = {detection_files}")

- name: Run ESCU Tests
run: |
cd security_content
echo "Content of contentctl.yml file: "
cat contentctl.yml
contentctl test --container-settings.num-containers 8 --post-test-behavior never_pause --disable-tqdm mode:selected --mode.files ${{ steps.filter-detection-files.outputs.DETECTION_FILES }}

- uses: actions/upload-artifact@v4
if: always()
with:
name: escu_test_summary_results
path: security_content/test_results/summary.yml

run-unit-tests:
name: test-unit-python3-${{ matrix.python-version }}
if: ${{ needs.test-inventory.outputs.unit == 'true' }}
Expand Down
72 changes: 72 additions & 0 deletions scripts/filter_escu_detections.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import yaml
import os
import configparser
import re

GITHUB_REPOSITORY = os.environ.get("GITHUB_REPOSITORY", "")

# Parse app.conf get the appid of the TA.
config = configparser.ConfigParser(strict=False)
config.read("package/default/app.conf")
APP_ID = config.get("id", "name")
APP_LABEL = config.get("ui", "label")

print(f"APP_ID = {APP_ID}, APP_LABEL = {APP_LABEL}")

# Read the file and remove trailing backslashes
with open("package/default/props.conf", "r") as f:
content = f.read()

# Remove trailing backslashes followed by a newline
updated_content = re.sub(r"\\\n", "", content)

# Write the cleaned content to a new file
with open("package/default/props.conf", "w") as f:
f.write(updated_content)

# Parse props.conf and collect all the sourcetypes in a list.
config = configparser.ConfigParser(strict=False)
config.read("package/default/props.conf")
sourcetypes = config.sections()

# Load the YAML content
with open("security_content/contentctl.yml", "r") as file:
data = yaml.safe_load(file)

app_found = False
for app in data["apps"]:
if app['appid'] == APP_ID or APP_ID in app['hardcoded_path'] or GITHUB_REPOSITORY in app['hardcoded_path'] or app["title"] == APP_LABEL or (app['appid'] == "PALO_ALTO_NETWORKS_ADD_ON_FOR_SPLUNK" and APP_ID == "Splunk_TA_paloalto_networks"):
app['hardcoded_path'] = "${{ env.TA_BUILD_PATH }}"
app_found = True

if not app_found:
print(f"App not found in contentctl.yml file. Exiting.")
exit(127)

# Write the modified data to the contentctl.yml file
with open("security_content/contentctl.yml", "w") as file:
yaml.dump(data, file, sort_keys=False)

# Filter out the detections based on the collected sourcetypes
base_dir = "security_content/detections"
detection_files = ""

for root, dirs, files in os.walk(base_dir):
for file in files:
file_path = os.path.join(root, file)

try:
with open(file_path, "r") as yaml_file:
file_content = yaml.safe_load(yaml_file)
if "deprecated" not in file_path and (
file_content["tests"][0]["attack_data"][0]["sourcetype"] in sourcetypes or file_content["tests"][0]["attack_data"][0]["source"] in sourcetypes):
detection_files += file_path.replace("security_content/", "") + " "

except Exception as e:
continue

# Save detection_files as an output variable
with open(os.getenv('GITHUB_OUTPUT'), 'w') as output_file:
output_file.write(f"DETECTION_FILES={detection_files}")

print(f"Filtered Detection files = {detection_files}")
Loading