From 1b0455b357155f3a554012ec0a9af5c0c445940d Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 18 May 2026 15:20:34 -0700 Subject: [PATCH 01/12] commit initial code snippets for faster new content generation --- .vscode/escu_6.code-snippets | 229 +++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 .vscode/escu_6.code-snippets diff --git a/.vscode/escu_6.code-snippets b/.vscode/escu_6.code-snippets new file mode 100644 index 0000000000..d5990d8b33 --- /dev/null +++ b/.vscode/escu_6.code-snippets @@ -0,0 +1,229 @@ +{ + "New Detection": { + "scope": "yaml", + "prefix": "detection_escu6", + "description": "Scaffold a Splunk Security Content detection YAML", + "body": [ + "name: ${1:${TM_FILENAME_BASE/(^|[_-])(\\w)/${1:+ }${2:/upcase}/g}}", + "id: $UUID", + "version: 1", + "creation_date: '${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}'", + "modification_date: '${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}'", + "author: ${2:Your Name, Splunk}", + "status: production", + "type: ${3|TTP,Anomaly,Hunting,Correlation|}", + "description: ${4}", + "data_source:", + " - ", + "search: >-", + " your search here", + " | `${TM_FILENAME_BASE}_filter`", + "how_to_implement: ", + "known_false_positives: ", + "references:", + " - ", + "drilldown_searches:", + " - name: View the detection results for - \"$risk_object$\"", + " search: '%original_detection_search% | search risk_object = \"$risk_object$\"'", + " earliest_offset: $info_min_time$", + " latest_offset: $info_max_time$", + " - name: View risk events for the last 7 days for - \"$risk_object$\"", + " search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN (\"$risk_object$\") | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as \"Search Name\" values(risk_message) as \"Risk Message\" values(analyticstories) as \"Analytic Stories\" values(annotations._all) as \"Annotations\" values(annotations.mitre_attack.mitre_tactic) as \"ATT&CK Tactics\" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`'", + " earliest_offset: 7d", + " latest_offset: \"0\"", + "finding:", + " title: ", + " entity:", + " field: ", + " type: ", + " score: ", + "intermediate_findings:", + " entities:", + " - field: ", + " type: ", + " score: ", + " message:", + "threat_objects:", + " - field: ", + " type: ", + "analytic_story:", + " - ", + "asset_type: ", + "mitre_attack_id:", + " - ", + "product:", + " - Splunk Enterprise", + " - Splunk Enterprise Security", + " - Splunk Cloud", + "category: ", + "security_domain: ", + "tests:", + " - name: True Positive Test", + " attack_data:", + " - data: ", + " source: ", + " sourcetype: ", + " test_type: unit", + ] + }, + "New Macro": { + "scope": "yaml", + "prefix": "macro_escu6", + "body": [ + "name: ${1:${TM_FILENAME_BASE/([_-])(\\w)/ ${2:/upcase}/g}}", + "id: $UUID", + "version: 1", + "creation_date: '${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}'", + "modification_date: '${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}'", + "author: ${1:Splunk Threat Research Team}", + "description: ${2}", + "definition: ${3}", + "$0" + ], + "description": "Scaffold a new Splunk security content macro YAML file" + }, + + "New Data Source": { + "scope": "yaml", + "prefix": "data_source_escu6", + "description": "Scaffold a new Splunk security content data source YAML file", + "body": [ + "name: ${1:${TM_FILENAME_BASE/(^|[_-])(\\w)/${1:+ }${2:/upcase}/g}}", + "id: $UUID", + "version: 1", + "creation_date: '${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}'", + "modification_date: '${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}'", + "author: ${1:Splunk Threat Research Team}", + "description: ${3}", + "source: ${4:sourcename}", + "sourcetype: ${5:sourcetype_value}", + "separator: ${6:EventCode}", + "separator_value: '${7}'", + "supported_TA:", + " - name: ${8}", + " url: ${9:hxxps://splunkbase.splunk.com/app/....}", + " version: ${10}", + "fields:", + " - ", + "output_fields:", + " - ", + "mitre_components:", + " - ", + "example_log: '${11}'", + "$0" + ] + }, + + "New Story": { + "scope": "yaml", + "prefix": "story_escu6", + "description": "Scaffold a new Splunk security content analytic story YAML file", + "body": [ + "name: ${1:${TM_FILENAME_BASE/(^|[_-])(\\w)/${1:+ }${2:/upcase}/g}}", + "id: $UUID", + "version: 1", + "creation_date: '${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}'", + "modification_date: '${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}'", + "author: ${2:Splunk Threat Research Team}", + "status: production", + "description: ${3}", + "narrative: ${4}", + "references:", + " - ", + "category:", + " - ", + "product:", + " - Splunk Enterprise", + " - Splunk Enterprise Security", + " - Splunk Cloud", + "usecase: ", + "$0" + ] + }, + + "New Lookup (CSV)": { + "scope": "yaml", + "prefix": "lookup_csv_escu6", + "description": "Scaffold a new Splunk security content CSV lookup YAML file", + "body": [ + "name: ${1:${TM_FILENAME_BASE/([_-])(\\w)/ ${2:/upcase}/g}}", + "id: $UUID", + "version: 1", + "creation_date: '${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}'", + "modification_date: '${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}'", + "author: ${2:Splunk Threat Research Team}", + "lookup_type: csv", + "description: ${3}", + "match_type:", + " - ${4:WILDCARD()}", + "min_matches: REMOVE ME IF NO MIN MATCHES REQUIRED", + "max_matches: REMOVE ME IF NO MAX MATCHES REQUIRED", + "case_sensitive_match: REMOVE ME IF NO CASE SENSITIVE MATCH REQUIRED", + "$0" + ] + }, + + "New Lookup (KVStore)": { + "scope": "yaml", + "prefix": "lookup_kvstore_escu6", + "description": "Scaffold a new Splunk security content KVStore lookup YAML file", + "body": [ + "name: ${1:${TM_FILENAME_BASE/([_-])(\\w)/ ${2:/upcase}/g}}", + "id: $UUID", + "version: 1", + "creation_date: '${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}'", + "modification_date: '${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}'", + "author: ${2:Splunk Threat Research Team}", + "lookup_type: kvstore", + "description: ${3}", + "fields:", + " - _key", + " - ${4:field_name}", + "$0" + ] + }, + + "New Dashboard": { + "scope": "yaml", + "prefix": "dashboard_escu6", + "description": "Scaffold a new Splunk security content dashboard YAML file", + "body": [ + "name: ${1:${TM_FILENAME_BASE/(^|[_-])(\\w)/${1:+ }${2:/upcase}/g}}", + "id: $UUID", + "version: 1", + "creation_date: '${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}'", + "modification_date: '${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}'", + "author: ${2:Splunk Threat Research Team}", + "description: ${3:Utilize this dashboard to...}", + "$0" + ] + }, + + "New Baseline": { + "scope": "yaml", + "prefix": "baseline_escu6", + "description": "Scaffold a new Splunk security content baseline YAML file", + "body": [ + "name: ${1:${TM_FILENAME_BASE/(^|[_-])(\\w)/${1:+ }${2:/upcase}/g}}", + "id: $UUID", + "version: 1", + "creation_date: '${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}'", + "modification_date: '${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}'", + "author: ${2:Splunk Threat Research Team}", + "status: production", + "description: ${3:This search...}", + "search: ${4}", + "how_to_implement: ${5}", + "known_false_positives: ${6}", + "references: []", + "product:", + " - Splunk Enterprise", + " - Splunk Enterprise Security", + " - Splunk Cloud", + "security_domain: ${7}", + "schedule: Default Baseline", + "$0" + ] + } + +} \ No newline at end of file From a139f9ab113b13760eeed4804c82f4a620e30e13 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 18 May 2026 16:20:46 -0700 Subject: [PATCH 02/12] commit new workflows for testing --- .github/actions/contentctl-build/action.yml | 34 +++++++++++++++++++ .github/workflows/appinspect.yml | 36 +++++++++------------ .github/workflows/build.yml | 34 ++++++------------- requirements.txt | 2 +- 4 files changed, 59 insertions(+), 47 deletions(-) create mode 100644 .github/actions/contentctl-build/action.yml diff --git a/.github/actions/contentctl-build/action.yml b/.github/actions/contentctl-build/action.yml new file mode 100644 index 0000000000..c84c87c227 --- /dev/null +++ b/.github/actions/contentctl-build/action.yml @@ -0,0 +1,34 @@ +name: contentctl-build +description: Set up Python, install build dependencies, and run contentctl-ng build. +inputs: + python-version: + description: Python version used for setup. + required: false + default: '3.14' + build-tarball-path: + description: Path to the built content tarball produced by contentctl-ng build. + required: false + default: dist/DA-ESS-ContentUpdate-latest.tar.gz +runs: + using: composite + steps: + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: ${{ inputs.python-version }} + architecture: x64 + cache: pip + cache-dependency-path: requirements.txt + + - name: Install build tool + shell: bash + run: | + echo "- Build Tool Version - $(cat requirements.txt)" + pip install -r requirements.txt + + - name: Run build + shell: bash + run: | + contentctl-ng build + mkdir -p artifacts + mv "${{ inputs.build-tarball-path }}" artifacts/ diff --git a/.github/workflows/appinspect.yml b/.github/workflows/appinspect.yml index 383715c130..09e7feee6a 100644 --- a/.github/workflows/appinspect.yml +++ b/.github/workflows/appinspect.yml @@ -1,45 +1,39 @@ +# This workflow performs a simple build action. +# It is intentionally separate from the build +# workflow to provide granular feedback and insight +# into when a build passes but an appinspect fails. name: appinspect + on: pull_request: types: [opened, reopened, synchronize] + jobs: appinspect: runs-on: ubuntu-latest steps: - - name: Check out repository (PR) - if: ${{ github.event_name == 'pull_request' }} + - name: Check out the repository code uses: actions/checkout@v6 - with: - ref: refs/pull/${{ github.event.pull_request.number }}/merge - - uses: actions/setup-python@v6 - with: - python-version: '3.11' - architecture: 'x64' - - - name: Install Python Dependencies and ContentCTL and Atomic Red Team - run: | - echo "- Contentctl version - $(cat requirements.txt)" - pip install -r requirements.txt - git clone --depth=1 --single-branch --branch=master https://github.com/redcanaryco/atomic-red-team.git external_repos/atomic-red-team - git clone --depth=1 --single-branch --branch="master" https://github.com/mitre-attack/attack-stix-data external_repos/cti + - name: Install dependencies and run build + uses: ./.github/actions/contentctl-build + - - name: Running appinspect with enrichments + - name: Run appinspect with enrichments env: APPINSPECTUSERNAME: "${{ secrets.APPINSPECTUSERNAME }}" APPINSPECTPASSWORD: "${{ secrets.APPINSPECTPASSWORD }}" run: | - echo $APPINSPECTUSERNAME - contentctl inspect --splunk-api-username "$APPINSPECTUSERNAME" --splunk-api-password "$APPINSPECTPASSWORD" --enrichments --enable-metadata-validation --suppress-missing-content-exceptions --enforce-deprecation-mapping-requirement - echo "done appinspect" + echo $APPINSPECTUSERNAME + contentctl-ng inspect --splunkbase-username "$APPINSPECTUSERNAME" --splunkbase-password "$APPINSPECTPASSWORD" mkdir -p artifacts/app_inspect_report cp -r dist/*.html artifacts/app_inspect_report cp -r dist/*.tar.gz artifacts/ - - name: store_artifacts + - name: Store artifacts uses: actions/upload-artifact@v7 with: - name: content-latest + name: content-latest-with-appinspect-report path: | artifacts/DA-ESS-ContentUpdate-latest.tar.gz artifacts/app_inspect_report diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a218f914bb..8f05e25118 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,10 +1,17 @@ +# This workflow performs a simple build action. +# It is intentionally separate from the appinspect +# workflow to provide granular feedback and insight +# into when a build passes but an appinspect fails. name: build + on: pull_request: types: [opened, reopened, synchronize] push: branches: - develop + + jobs: build: runs-on: ubuntu-latest @@ -12,28 +19,5 @@ jobs: - name: Check out the repository code uses: actions/checkout@v6 - - uses: actions/setup-python@v6 - with: - python-version: '3.11' - architecture: 'x64' - - - name: Install Python Dependencies and ContentCTL and Atomic Red Team - run: | - echo "- Contentctl version - $(cat requirements.txt)" - pip install -r requirements.txt - git clone --depth=1 --single-branch --branch=master https://github.com/redcanaryco/atomic-red-team.git external_repos/atomic-red-team - git clone --depth=1 --single-branch --branch="master" https://github.com/mitre-attack/attack-stix-data external_repos/cti - - - name: Running build with enrichments - run: | - contentctl build --enrichments --enforce_deprecation_mapping_requirement - mkdir artifacts - mv dist/DA-ESS-ContentUpdate-latest.tar.gz artifacts/ - - - name: store_artifacts - uses: actions/upload-artifact@v7 - with: - name: content-latest - path: | - artifacts/DA-ESS-ContentUpdate-latest.tar.gz - dist/api + - name: Install dependencies and run build + uses: ./.github/actions/contentctl-build \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 4fc98151bf..5b211c22ba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -contentctl==5.6.0 +contentctl-ng==0.2.0 From 88497f86a45072eff3ec4076d8be67706d35a200 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 18 May 2026 16:24:46 -0700 Subject: [PATCH 03/12] Fix syntax issue with appinspect workflow --- .github/workflows/appinspect.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/appinspect.yml b/.github/workflows/appinspect.yml index 09e7feee6a..50753c34d8 100644 --- a/.github/workflows/appinspect.yml +++ b/.github/workflows/appinspect.yml @@ -24,8 +24,7 @@ jobs: APPINSPECTUSERNAME: "${{ secrets.APPINSPECTUSERNAME }}" APPINSPECTPASSWORD: "${{ secrets.APPINSPECTPASSWORD }}" run: | - echo $APPINSPECTUSERNAME - contentctl-ng inspect --splunkbase-username "$APPINSPECTUSERNAME" --splunkbase-password "$APPINSPECTPASSWORD" + contentctl-ng inspect --splunkbase-username "$APPINSPECTUSERNAME" --splunkbase-password "$APPINSPECTPASSWORD" mkdir -p artifacts/app_inspect_report cp -r dist/*.html artifacts/app_inspect_report cp -r dist/*.tar.gz artifacts/ From b31f1d8c211f6e4692b23a73fb0e0313e83a29c3 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 18 May 2026 16:54:15 -0700 Subject: [PATCH 04/12] Split the build and install action into two different actions, build and install separately. This gives us better control and more reusability over them. Update the unit-testing workflow --- .github/actions/contentctl-build/action.yml | 21 ++--------- .github/actions/contentctl-install/action.yml | 22 ++++++++++++ .github/workflows/unit-testing.yml | 35 +++++++++++-------- 3 files changed, 45 insertions(+), 33 deletions(-) create mode 100644 .github/actions/contentctl-install/action.yml diff --git a/.github/actions/contentctl-build/action.yml b/.github/actions/contentctl-build/action.yml index c84c87c227..826ad4317d 100644 --- a/.github/actions/contentctl-build/action.yml +++ b/.github/actions/contentctl-build/action.yml @@ -1,10 +1,6 @@ name: contentctl-build -description: Set up Python, install build dependencies, and run contentctl-ng build. +description: Install contentctl-ng and run contentctl-ng build. inputs: - python-version: - description: Python version used for setup. - required: false - default: '3.14' build-tarball-path: description: Path to the built content tarball produced by contentctl-ng build. required: false @@ -12,19 +8,8 @@ inputs: runs: using: composite steps: - - name: Set up Python - uses: actions/setup-python@v6 - with: - python-version: ${{ inputs.python-version }} - architecture: x64 - cache: pip - cache-dependency-path: requirements.txt - - - name: Install build tool - shell: bash - run: | - echo "- Build Tool Version - $(cat requirements.txt)" - pip install -r requirements.txt + - name: Install contentctl-ng + uses: ./.github/actions/contentctl-install - name: Run build shell: bash diff --git a/.github/actions/contentctl-install/action.yml b/.github/actions/contentctl-install/action.yml new file mode 100644 index 0000000000..99c74d2030 --- /dev/null +++ b/.github/actions/contentctl-install/action.yml @@ -0,0 +1,22 @@ +name: contentctl-install +description: Set up Python and install contentctl-ng from requirements.txt. +inputs: + python-version: + description: Python version used for setup. + required: false + default: '3.14' + +runs: + using: composite + steps: + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: ${{ inputs.python-version }} + architecture: x64 + + - name: Install contentctl-ng + shell: bash + run: | + echo "- Build Tool Version - $(cat requirements.txt)" + pip install -r requirements.txt diff --git a/.github/workflows/unit-testing.yml b/.github/workflows/unit-testing.yml index 8fa96affb8..c6bf06ec72 100644 --- a/.github/workflows/unit-testing.yml +++ b/.github/workflows/unit-testing.yml @@ -15,20 +15,11 @@ jobs: ref: ${{ github.base_ref }} - - uses: actions/setup-python@v6 - with: - python-version: '3.11' #Available versions here - https://github.com/actions/python-versions/releases easy to change/make a matrix/use pypy - architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified - - name: Install Python Dependencies and ContentCTL - run: | - python -m pip install --upgrade pip - echo "- Contentctl version - $(cat requirements.txt)" - pip install -r requirements.txt - - - # Running contentctl test with a few arguments, before running the command make sure you checkout into the current branch of the pull request. This step only performs unit testing on all the changes against the target-branch. In most cases this target branch will be develop - # Make sure we check out the PR, even if it actually lives in a fork + uses: ./.github/actions/contentctl-install + + + # Check out the PR, even if it lives in a fork. # Instructions for pulling a PR were taken from: # https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally - name: Run ContentCTL test for changes against target branch @@ -41,7 +32,21 @@ jobs: git fetch origin pull/${{ github.event.pull_request.number }}/head:new_branch_for_testing #We must specifically get the PR's target branch from security_content, not the one that resides in the fork PR's forked repo git switch new_branch_for_testing - contentctl test --verbose --disable-tqdm --no-enable-integration-testing --container-settings.num-containers 1 --post-test-behavior never_pause mode:changes --mode.target-branch ${{ github.base_ref }} + + + - name: Run a contentctl-ng build to ensure that the branch in syntactically correct + uses: ./.github/actions/contentctl-build + + - name: Run a contentctl-ng install to configure the testing environment + env: + APPINSPECTUSERNAME: "${{ secrets.APPINSPECTUSERNAME }}" + APPINSPECTPASSWORD: "${{ secrets.APPINSPECTPASSWORD }}" + run : | + contentctl-ng install --splunkbase-username "$APPINSPECTUSERNAME" --splunkbase-password "$APPINSPECTPASSWORD" + + - name: Test content which has changed between this branch and the target branch + run: | + contentctl-ng test --verbose --test-type UNIT --post-test-behavior NEVER_PAUSE --mode CHANGED --git-ref ${{ github.base_ref }} echo "contentctl test - COMPLETED" # Store test_results/summary.yml and dist/DA-ESS-ContentUpdate-latest.tar.gz to job artifact-test_summary_results.zip @@ -65,4 +70,4 @@ jobs: run: | echo "This job will fail if there are failures in unit-testing" python .github/workflows/format_test_results.py >> $GITHUB_STEP_SUMMARY - echo "The Unit testing is completed. See details in the unit-testing job summary UI " \ No newline at end of file + echo "The Unit testing is completed. See details in the unit-testing job summary UI " From d071304178efd134bc730626ff92b937030fc40b Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 18 May 2026 17:12:31 -0700 Subject: [PATCH 05/12] stage the most recent release to run appinspect metadata validation against --- .github/workflows/unit-testing.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/unit-testing.yml b/.github/workflows/unit-testing.yml index c6bf06ec72..ec921efd3d 100644 --- a/.github/workflows/unit-testing.yml +++ b/.github/workflows/unit-testing.yml @@ -42,7 +42,9 @@ jobs: APPINSPECTUSERNAME: "${{ secrets.APPINSPECTUSERNAME }}" APPINSPECTPASSWORD: "${{ secrets.APPINSPECTPASSWORD }}" run : | - contentctl-ng install --splunkbase-username "$APPINSPECTUSERNAME" --splunkbase-password "$APPINSPECTPASSWORD" + # Get the most recent release from GitHub + curl -o DA-ESS-ContentUpdate-latest-github-release.tar.gz https://github.com/splunk/security_content/releases/latest/download/DA-ESS-ContentUpdate-latest.tar.gz + contentctl-ng inspect --splunkbase-username "$APPINSPECTUSERNAME" --splunkbase-password "$APPINSPECTPASSWORD" --old-app-path DA-ESS-ContentUpdate-latest-github-release.tar.gz - name: Test content which has changed between this branch and the target branch run: | From 332f382bd3c2d2d4652b2e02e5f56598e63a7ee8 Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 19 May 2026 12:00:26 -0700 Subject: [PATCH 06/12] update appinspect to diff against most recent release --- .github/workflows/appinspect.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/appinspect.yml b/.github/workflows/appinspect.yml index 50753c34d8..524a925585 100644 --- a/.github/workflows/appinspect.yml +++ b/.github/workflows/appinspect.yml @@ -24,7 +24,11 @@ jobs: APPINSPECTUSERNAME: "${{ secrets.APPINSPECTUSERNAME }}" APPINSPECTPASSWORD: "${{ secrets.APPINSPECTPASSWORD }}" run: | - contentctl-ng inspect --splunkbase-username "$APPINSPECTUSERNAME" --splunkbase-password "$APPINSPECTPASSWORD" + # Download the most recent Release in order to enforce metadata validation + curl -o DA-ESS-ContentUpdate-latest.tar.gz https://github.com/splunk/security_content/releases/latest/download/DA-ESS-ContentUpdate-latest.tar.gz + + # Inspect, using the release we downloaded above + contentctl-ng inspect --splunkbase-username "$APPINSPECTUSERNAME" --splunkbase-password "$APPINSPECTPASSWORD" --old-app-path DA-ESS-ContentUpdate-latest.tar.gz mkdir -p artifacts/app_inspect_report cp -r dist/*.html artifacts/app_inspect_report cp -r dist/*.tar.gz artifacts/ From 621df1dab7c690a0cf15af90b72873235d7ad11b Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 19 May 2026 12:07:54 -0700 Subject: [PATCH 07/12] update curl command for latest app download --- .github/workflows/appinspect.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/appinspect.yml b/.github/workflows/appinspect.yml index 524a925585..84d5f7196c 100644 --- a/.github/workflows/appinspect.yml +++ b/.github/workflows/appinspect.yml @@ -25,7 +25,8 @@ jobs: APPINSPECTPASSWORD: "${{ secrets.APPINSPECTPASSWORD }}" run: | # Download the most recent Release in order to enforce metadata validation - curl -o DA-ESS-ContentUpdate-latest.tar.gz https://github.com/splunk/security_content/releases/latest/download/DA-ESS-ContentUpdate-latest.tar.gz + # --location flag required to follow redirects from the "latest" URL + curl --location -o DA-ESS-ContentUpdate-latest.tar.gz https://github.com/splunk/security_content/releases/latest/download/DA-ESS-ContentUpdate-latest.tar.gz # Inspect, using the release we downloaded above contentctl-ng inspect --splunkbase-username "$APPINSPECTUSERNAME" --splunkbase-password "$APPINSPECTPASSWORD" --old-app-path DA-ESS-ContentUpdate-latest.tar.gz From f74364d7a7f04e2b1e1ae4fa63added9ca6d36a1 Mon Sep 17 00:00:00 2001 From: Eric McGinnis Date: Tue, 19 May 2026 17:38:59 -0700 Subject: [PATCH 08/12] updated to latest cicd release --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 5b211c22ba..3f5969e0e4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -contentctl-ng==0.2.0 +contentctl-ng==0.3.0 From 894c64454a44c0114f5653ca6234064760108645 Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 20 May 2026 09:27:20 -0700 Subject: [PATCH 09/12] Update unit testing workflow --- .github/workflows/unit-testing.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/unit-testing.yml b/.github/workflows/unit-testing.yml index ec921efd3d..fbefce2aae 100644 --- a/.github/workflows/unit-testing.yml +++ b/.github/workflows/unit-testing.yml @@ -22,7 +22,7 @@ jobs: # Check out the PR, even if it lives in a fork. # Instructions for pulling a PR were taken from: # https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally - - name: Run ContentCTL test for changes against target branch + - name: Checkout the PR branch and the target to calculate changed files for testing run: | echo "Current Branch (Head Ref): ${{ github.head_ref }}" @@ -37,18 +37,22 @@ jobs: - name: Run a contentctl-ng build to ensure that the branch in syntactically correct uses: ./.github/actions/contentctl-build + - name: Start the test environment + run: | + docker run -d --platform linux/amd64 -p 8088:8088 -p 8089:8089 -p 8000:8000 -e 'SPLUNK_START_ARGS=--accept-license' -e 'SPLUNK_GENERAL_TERMS=--accept-sgt-current-at-splunk-com' -e 'SPLUNK_PASSWORD=Chang3d!' --name splunk splunk/splunk:latest + # Wait some time for this environment to be ready + sleep 180 + - name: Run a contentctl-ng install to configure the testing environment env: APPINSPECTUSERNAME: "${{ secrets.APPINSPECTUSERNAME }}" APPINSPECTPASSWORD: "${{ secrets.APPINSPECTPASSWORD }}" run : | - # Get the most recent release from GitHub - curl -o DA-ESS-ContentUpdate-latest-github-release.tar.gz https://github.com/splunk/security_content/releases/latest/download/DA-ESS-ContentUpdate-latest.tar.gz - contentctl-ng inspect --splunkbase-username "$APPINSPECTUSERNAME" --splunkbase-password "$APPINSPECTPASSWORD" --old-app-path DA-ESS-ContentUpdate-latest-github-release.tar.gz + contentctl-ng install --splunkbase-username "$APPINSPECTUSERNAME" --splunkbase-password "$APPINSPECTPASSWORD" - name: Test content which has changed between this branch and the target branch run: | - contentctl-ng test --verbose --test-type UNIT --post-test-behavior NEVER_PAUSE --mode CHANGED --git-ref ${{ github.base_ref }} + contentctl-ng test --verbose --post-test-behavior NEVER_PAUSE --mode CHANGED --git-ref ${{ github.base_ref }} echo "contentctl test - COMPLETED" # Store test_results/summary.yml and dist/DA-ESS-ContentUpdate-latest.tar.gz to job artifact-test_summary_results.zip From 8a07834c31170acbe0361ab75ac413634d585ddc Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 20 May 2026 09:47:24 -0700 Subject: [PATCH 10/12] run on larger runner --- .github/workflows/unit-testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit-testing.yml b/.github/workflows/unit-testing.yml index fbefce2aae..266b05652f 100644 --- a/.github/workflows/unit-testing.yml +++ b/.github/workflows/unit-testing.yml @@ -4,7 +4,7 @@ on: types: [opened, reopened, synchronize] jobs: unit-testing: - runs-on: ubuntu-latest + runs-on: large-ubuntu-22.04-32core if: "!contains(github.ref, 'refs/tags/')" #don't run on tags - future steps won't run either since they depend on this job steps: #For fork PRs, always check out security_content and the PR target in security content! From 04c0c1a500a728e46ba9cba318360e6b88ede9a9 Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 20 May 2026 10:31:03 -0700 Subject: [PATCH 11/12] add contentctl.yml back as it is required for legacy testing workflow operations. --- contentctl.yml | 268 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 268 insertions(+) create mode 100644 contentctl.yml diff --git a/contentctl.yml b/contentctl.yml new file mode 100644 index 0000000000..b40b943e4d --- /dev/null +++ b/contentctl.yml @@ -0,0 +1,268 @@ +path: . +app: + uid: 3449 + title: ES Content Updates + appid: DA-ESS-ContentUpdate + version: 6.0.0 + description: Explore the Analytic Stories included with ES Content Updates. + prefix: ESCU + label: ESCU + author_name: Splunk Threat Research Team + author_email: research@splunk.com + author_company: Splunk +enrichments: false +build_app: true +build_api: true +build_ssa: false +build_path: dist +test_instance: + splunk_app_username: admin + instance_address: localhost + hec_port: 8088 + web_ui_port: 8000 + api_port: 8089 +container_settings: + full_image_path: registry.hub.docker.com/splunk/splunk:9.3 + leave_running: true + num_containers: 1 +mode: {} +splunk_api_username: null +post_test_behavior: pause_on_failure +apps: +- uid: 1621 + title: Splunk_SA_CIM + appid: Splunk_SA_CIM + version: 8.5.0 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-common-information-model-cim_850.tgz +- uid: 6553 + title: Splunk Add-on for Okta Identity Cloud + appid: Splunk_TA_okta_identity_cloud + version: 5.0.2 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-okta-identity-cloud_502.tgz +- uid: 7404 + title: Cisco Security Cloud + appid: CiscoSecurityCloud + version: 3.6.5 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/cisco-security-cloud_365.tgz +- uid: 7569 + title: Cisco Secure Access Add-on for Splunk + appid: TA-cisco-cloud-security-addon + version: 1.0.50 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/cisco-secure-access-add-on-for-splunk_1050.tar.gz +- uid: 6652 + title: Add-on for Linux Sysmon + appid: Splunk_TA_linux_sysmon + version: 1.0.0 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-sysmon-for-linux_100.tgz +- uid: null + title: Splunk Fix XmlWinEventLog HEC Parsing + appid: Splunk_FIX_XMLWINEVENTLOG_HEC_PARSING + version: '0.1' + description: This TA is required for replaying Windows Data into the Test Environment. + The Default TA does not include logic for properly splitting multiple log events + in a single file. In production environments, this logic is applied by the Universal + Forwarder. + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/Splunk_TA_fix_windows.tgz +- uid: 742 + title: Splunk Add-on for Microsoft Windows + appid: SPLUNK_ADD_ON_FOR_MICROSOFT_WINDOWS + version: 10.0.1 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-microsoft-windows_1001.tgz +- uid: 5709 + title: Splunk Add-on for Sysmon + appid: Splunk_TA_microsoft_sysmon + version: 5.0.0 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-sysmon_500.tgz +- uid: 833 + title: Splunk Add-on for Unix and Linux + appid: Splunk_TA_nix + version: 10.2.0 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-unix-and-linux_1020.tgz +- uid: 5579 + title: Splunk Add-on for CrowdStrike FDR + appid: Splunk_TA_CrowdStrike_FDR + version: 2.0.5 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-crowdstrike-fdr_205.tgz +- uid: 3185 + title: Splunk Add-on for Microsoft IIS + appid: SPLUNK_TA_FOR_IIS + version: 1.3.0 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-microsoft-iis_130.tgz +- uid: 6994 + title: CCX Add-on for Suricata + appid: SPLUNK_TA_FOR_SURICATA + version: 1.0.1 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/ccx-add-on-for-suricata_101.tgz +- uid: 5466 + title: TA for Zeek + appid: SPLUNK_TA_FOR_ZEEK + version: 1.0.11 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/ta-for-zeek_1011.tgz +- uid: 3258 + title: Splunk Add-on for NGINX + appid: SPLUNK_ADD_ON_FOR_NGINX + version: 3.3.0 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-nginx_330.tgz +- uid: 5238 + title: Splunk Add-on for Stream Forwarders + appid: SPLUNK_ADD_ON_FOR_STREAM_FORWARDERS + version: 8.1.3 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-stream-forwarders_813.tgz +- uid: 5234 + title: Splunk Add-on for Stream Wire Data + appid: SPLUNK_ADD_ON_FOR_STREAM_WIRE_DATA + version: 8.1.6 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-stream-wire-data_816.tgz +- uid: 2757 + title: Splunk Add-on for Palo Alto Networks + appid: SPLUNK_ADD_ON_FOR_PALO_ALTO_NETWORKS + version: 3.0.1 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-palo-alto-networks_301.tgz +- uid: 3865 + title: Zscaler Technical Add-On for Splunk + appid: Zscaler_CIM + version: 4.0.16 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/zscaler-technical-add-on-for-splunk_4016.tgz +- uid: 3719 + title: Splunk Add-on for Amazon Kinesis Firehose + appid: SPLUNK_ADD_ON_FOR_AMAZON_KINESIS_FIREHOSE + version: 1.3.2 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-amazon-kinesis-firehose_132.tgz +- uid: 1876 + title: Splunk Add-on for AWS + appid: Splunk_TA_aws + version: 8.1.1 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-amazon-web-services-aws_811.tgz +- uid: 3088 + title: Splunk Add-on for Google Cloud Platform + appid: SPLUNK_ADD_ON_FOR_GOOGLE_CLOUD_PLATFORM + version: 4.7.0 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-google-cloud-platform_470.tgz +- uid: 5556 + title: Splunk Add-on for Google Workspace + appid: SPLUNK_ADD_ON_FOR_GOOGLE_WORKSPACE + version: 3.1.1 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-google-workspace_311.tgz +- uid: 3110 + title: Splunk Add-on for Microsoft Cloud Services + appid: SPLUNK_TA_MICROSOFT_CLOUD_SERVICES + version: 6.1.1 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-microsoft-cloud-services_611.tgz +- uid: 4055 + title: Splunk Add-on for Microsoft Office 365 + appid: SPLUNK_ADD_ON_FOR_MICROSOFT_OFFICE_365 + version: 6.0.2 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-microsoft-office-365_602.tgz +- uid: 5518 + title: Splunk add on for Microsoft Defender Advanced Hunting + appid: SPLUNK_ADD_ON_FOR_MICROSOFT_DEFENDER_ADVANCED_HUNTING + version: 1.4.2 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/microsoft-defender-advanced-hunting-add-on-for-splunk_142.tgz +- uid: 6207 + title: Splunk Add-on for Microsoft Security + appid: Splunk_TA_MS_Security + version: 3.0.0 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-microsoft-security_300.tgz +- uid: 2734 + title: URL Toolbox + appid: URL_TOOLBOX + version: 1.9.4 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/url-toolbox_194.tgz +- uid: 6853 + title: Splunk Add-on for Admon Enrichment + appid: SA-admon + version: 1.1.2 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-admon-enrichment_112.tgz +- uid: 5082 + title: CrowdStrike Falcon Event Streams Technical Add-On + appid: TA-crowdstrike-falcon-event-streams + version: 3.2.1 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/crowdstrike-falcon-event-streams-technical-add-on_321.tgz +- uid: 6254 + title: Splunk Add-on for Github + appid: Splunk_TA_github + version: 3.2.0 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-github_320.tgz +- uid: 3471 + title: Splunk Add-on for AppDynamics + appid: Splunk_TA_AppDynamics + version: 3.2.1 + description: The Splunk Add-on for AppDynamics enables you to easily configure data + inputs to pull data from AppDynamics' REST APIs + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/cisco-splunk-add-on-for-appdynamics_321.tgz +- uid: 4221 + title: Cisco NVM Add-on for Splunk + appid: TA-Cisco-NVM + version: 4.0.7 + description: The Cisco Endpoint Security Analytics (CESA) Add-On for Splunk allows + IT administrators to analyze and correlate user and endpoint behavior in Splunk + Enterprise. This Add-on provides configuration and collection of data from the + Cisco AnyConnect Network Visibility Module IPFIX (nvzFlow) Collector. This module + collects additional context such as user, device, application, location and destination + for flows both on and off premise. + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/cisco-endpoint-security-analytics-cesa-add-on-for-splunk_407.tgz +- uid: 5603 + title: Add-on for VMware ESXi Logs + appid: Splunk_TA_esxilogs + version: 4.2.2 + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-vmware-esxi-logs_422.tgz +- uid: 5640 + title: Splunk Add-on for VMware Indexes + appid: SPLUNK_ADD_ON_FOR_VMWARE_INDEXES + version: 4.0.3 + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-vmware-indexes_403.tgz +- uid: 1467 + title: Cisco Networks Add-on + appid: TA-cisco_ios + version: 2.7.9 + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/add-on-for-cisco-network-data_279.tgz +- uid: 8024 + title: TA-ollama + appid: ta-ollama + version: 0.1.5 + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/ta-ollama_015.tgz +- uid: 8377 + title: MCP TA + appid: mcp-ta + version: 0.1.2 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/mcp-ta_012.tgz +- uid: 8574 + title: TA-osquery + appid: ta-osquery + version: 1.0.4 + description: description of app + hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/ta-osquery_104.tgz +githash: d6fac80e6d50ae06b40f91519a98489d4ce3a3fd +test_data_caches: +- base_url: https://media.githubusercontent.com/media/splunk/attack_data/master/ + base_directory_name: external_repos/attack_data From 9e7f0ff60eae4aeda493e11cda8ce2558b602774 Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Wed, 20 May 2026 18:53:35 -0700 Subject: [PATCH 12/12] update to contentctl-ng 0.4.0 update inspect workflow to ignore missing detections --- .github/workflows/appinspect.yml | 4 +++- requirements.txt | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/appinspect.yml b/.github/workflows/appinspect.yml index 84d5f7196c..9e36ec1e27 100644 --- a/.github/workflows/appinspect.yml +++ b/.github/workflows/appinspect.yml @@ -29,7 +29,9 @@ jobs: curl --location -o DA-ESS-ContentUpdate-latest.tar.gz https://github.com/splunk/security_content/releases/latest/download/DA-ESS-ContentUpdate-latest.tar.gz # Inspect, using the release we downloaded above - contentctl-ng inspect --splunkbase-username "$APPINSPECTUSERNAME" --splunkbase-password "$APPINSPECTPASSWORD" --old-app-path DA-ESS-ContentUpdate-latest.tar.gz + # Also, we intentionally ignore missing content exceptions - private content is + # not expected to be present when running this check in GitHub + contentctl-ng inspect --splunkbase-username "$APPINSPECTUSERNAME" --splunkbase-password "$APPINSPECTPASSWORD" --old-app-path DA-ESS-ContentUpdate-latest.tar.gz --ignore-missing-content-exceptions mkdir -p artifacts/app_inspect_report cp -r dist/*.html artifacts/app_inspect_report cp -r dist/*.tar.gz artifacts/ diff --git a/requirements.txt b/requirements.txt index 3f5969e0e4..1be27eb086 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -contentctl-ng==0.3.0 +contentctl-ng==0.4.0