If incase you have followe d my previous projects and have done with the projects lets move even more further advancing it by integrating the secure ci/cd project with soc automation

by now you would have understood to start the vm connect to your ssh of wazuh and thehive Start your VMs and on the Linux machine where your SOC tools are installed, run these commands one by one:

this are commands below to restart you're wazuh instance 

sudo systemctl start wazuh-manager
sudo systemctl status wazuh-manager
this commands are for the hive 
sudo systemctl start elasticsearch
sudo systemctl status elasticsearch
sudo systemctl start filebeat
sudo systemctl status filebeat
sudo systemctl start cassandra
sudo systemctl status cassandra
sudo systemctl start thehive
sudo systemctl status thehive
None
None
None

Check your GitHub repo for the Secure CI/CD project

Now do only this:

Open your GitHub repository for the secure CI/CD project and check whether these are already present:

  • sample app code
  • .github/workflows/
  • any workflow file like ci.yml or main.yml
  • check in ci.yml if the pipeline has this below steps ready. SAST,secrets scan,dependency scan,DAST,output/report handling
None

Add one more security scan for dependency vulnerabilities

Right now you have:

  • SAST → Semgrep
  • DAST → OWASP ZAP

But for proper secure CI/CD integration, you also need SCA for vulnerable packages.

      - name: Run pip-audit Scan
        run: |
          pip install pip-audit
          pip-audit

we are adding SCA (software composition analysis) basically this checks the packages you installed is safe or not (I hope you remeber where do we do this changes in )

if incase the VS code doesn't show up the semgrep scan try adding it again

1) Add Semgrep after Run app syntax check
      - name: Run Semgrep Scan
        run: |
          pip install semgrep
          semgrep scan --config auto

2) Add Flask start and OWASP ZAP after Scan Docker image with Trivy
      - name: Start Flask App
        run: |
          nohup python app.py &
          sleep 10

      - name: Run OWASP ZAP Scan
        uses: zaproxy/action-baseline@v0.12.0
        with:
          target: 'http://127.0.0.1:5000'
>run it in ther terminal of VS code 

git add .github/workflows/ci.yml
git commit -m "Add Semgrep and OWASP ZAP to CI pipeline"
git push
None
you can check this through >github>action>build and test >run semgrep

we did find the semgrep is already finding an issuers of the projects

now change this above semgrep scan to this line so it converta to json file and send it to the wazuh   

- name: Run Semgrep Scan
        run: |
          pip install semgrep
          semgrep scan --config auto --json --output semgrep-report.json
git add .github/workflows/ci.yml
git commit -m "Generate Semgrep JSON report"
git push
None
None

Add one more step right after the Semgrep scan to upload that JSON file as a GitHub Actions artifact.

Paste this into ci.yml after the Semgrep step:

- name: Upload Semgrep report
        uses: actions/upload-artifact@v4
        with:
          name: semgrep-report
          path: semgrep-report.json

This saves the Semgrep report file in the workflow run so later we can use it for SOC integration.

None

git status
git add .github/workflows/ci.yml
git commit -m "semgrep uploaded file"
git push
None
None

In the above picture it means Semgrep JSON report file was successfully uploaded in GitHub Actions as an artifact named semgrep-report.

So now the scan result is not only shown on screen, it is also saved as a downloadable file, which we can later use to connect with Wazuh / SOC automation.

Add another step in ci.yml to also create a simple text log file from the Semgrep report. This is useful because later it is easier to forward a simple log/text file into Wazuh.

Add this block after Upload Semgrep report:

Add another step in ci.yml to also create a simple text log file from the Semgrep report.
This is useful because later it is easier to forward a simple log/text file into Wazuh.

Add this block after Upload Semgrep report:

Add this block right after Convert Semgrep JSON to text log:

- name: Upload Semgrep text log
        uses: actions/upload-artifact@v4
        with:
          name: semgrep-log
          path: semgrep-report.log
git add .github/workflows/ci.yml
git commit -m "Upload Semgrep text log artifact"
git push
None

Now let's move to Wazuh

sudo mkdir -p /var/log/semgrep
sudo touch /var/log/semgrep/semgrep-report.log
sudo chmod 644 /var/log/semgrep/semgrep-report.log
ls -l /var/log/semgrep/

this creates the log location where wazuh will read the semgrep findings

None
sudo nano /var/ossec/etc/ossec.conf
<localfile>
  <log_format>syslog</log_format>
  <location>/var/log/semgrep/semgrep-report.log</location>
</localfile>

ossec.conf is the main Wazuh configuration file. It tells Wazuh what logs to watch, where to read them from and how to process them

None
add it in the end of the file

Now do only this on the Wazuh server:

sudo systemctl restart wazuh-manager
sudo systemctl status wazuh-manager
None

What proves the change was loaded is this line:

  • Active: active (running)

and also the recent restart time:

  • since Mon 2026–04–13 20:23:11 UTC; 24s ago

That means Wazuh restarted successfully after you edited ossec.conf, so it has reloaded the updated configuration.

Open the decoder/rules file area and first check whether a local rules file already exists:

ls -l /var/ossec/etc/rules/

What we are going to do next is create a custom Wazuh rule so that when Semgrep log lines appear, Wazuh can recognize them as a security alert.

None
local_rules.xml already exists, so we will use this file for the custom Semgrep alert rule.
<rule id="100500" level="10">
  <match>Blocking</match>
  <description>Semgrep security finding detected</description>
  <group>semgrep,ci_cd,security_alert,</group>
</rule>
  • rule id is just a unique number for each Wazuh rule. It should not repeat another rule number. It is not fully random, but people often choose a custom number range like 100500, 100501, etc for their own rules.
  • match tells Wazuh what word or text to look for in the log. In your case, if it sees Blocking, it triggers the rule.
  • group is like a tag/category name. It helps organize alerts, like putting them into folders such as semgrep, ci_cd, and security_alert
None

Now we need to test whether Wazuh can detect a Semgrep-style finding.

On the Wazuh server, do only this:

echo "Blocking Semgrep security finding detected in CI/CD pipeline" | sudo tee -a /var/log/semgrep/semgrep-report.log

Why

This writes a test line into the Semgrep log file. Your custom Wazuh rule looks for the word Blocking, so this should trigger an alert.

None
None
we see wazuh has detected semgrep
  • rule.description: Semgrep security finding detected
  • rule.id: 100500
  • rule.groups: semgrep, ci_cd, security_alert
  • location: /var/log/semgrep/semgrep-report.log

That means the CI/CD → Semgrep log → Wazuh alert part is now working.

None
None

click on thehive

{
  "title": "CI/CD Semgrep Alert",
  "description": "Semgrep security finding received from Wazuh",
  "severity": 3,
  "source": "Wazuh",
  "sourceRef": "semgrep-test-1",
  "type": "external",
  "tags": ["semgrep", "cicd", "wazuh"]
}
None
None
None
copy this uri and paste it in ossec.conf of wazuh
sudo nano /var/ossec/etc/ossec.conf
<integration>
  <name>custom-webhook</name>
  <hook_url>https://shuffler.io/api/v1/hooks/webhook_9db2dd6a-cebf-4ce1-a3f4-758c751b7396</hook_url>
  <level>10</level>
  <rule_id>100500</rule_id>
  <alert_format>json</alert_format>
</integration>
sudo systemctl restart wazuh-manager
sudo systemctl status wazuh-manager

On the Wazuh server, trigger the test alert one more time so Wazuh sends it to Shuffle:

echo "Blocking Semgrep security finding detected in CI/CD pipeline

We are triggering it manually just for testing.

That command writes a fake Semgrep-style alert line into the log file so we can verify the full path works:

log file → Wazuh reads it → rule matches Blocking → alert is created → sent to Shuffle → then TheHive/email

In the real project, this line would not be typed by you manually. It would come from your CI/CD pipeline output/report

so incase you face an error in the execution in shuffle try running this command and check

curl -X POST "https://shuffler.io/api/v1/hooks/webhook_dd66026c-22ef-47db-acc4-d0eea74008cc" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "CI/CD Semgrep Alert",
    "description": "Test alert sent directly from Wazuh server",
    "source": "Wazuh",
    "sourceRef": "semgrep-direct-test-1",
    "severity": 3,
    "type": "external",
    "tags": ["semgrep","cicd","wazuh"]
  }'
None
None
None

now we see that manual test is working so now we need to make changes in github ci.yml for automatically to trigger whenever we run semgrep

In your GitHub Actions ci.yml, add this step after Convert Semgrep JSON to text log:

- name: Create simple Semgrep alert line
        run: |
          echo "Blocking Semgrep security finding detected in CI/
git add .github/workflows/ci.yml
git commit -m "Update GitHub Actions workflow"
git push origin advanced-devsecops

add this line after create simple semgrep alert line

     - name: Send alert to Shuffle webhook (only if issues found)
        run: |
          if grep -q '"results": \[\]' semgrep-report.json; then
            echo "No issues found, skipping alert"
          else
            curl -X POST "YOUR_SHUFFLE_WEBHOOK_URL" \
            -H "Content-Type: application/json" \
            -d "{\"title\":\"CI/CD Semgrep Alert\",\"description\":\"Semgrep security finding detected in CI/CD pipeline\",\"severity\":3,\"source\":\"GitHub Actions\",\"sourceRef\":\"semgrep-test-1\",\"type\":\"external\",\"tags\":[\"semgrep\",\"cicd\",\"github-actions\"]}"
          fi
git add .github/workflows/ci.yml
git commit -m "Add Shuffle webhook step"
git push origin advanced-devsecops

Change the TheHive alert body from fixed test text to slightly better values.

Use this:

{
  "title": "CI/CD Semgrep Alert",
  "description": "Semgrep security finding detected from CI/CD pipeline and forwarded by Wazuh",
  "severity": 3,
  "source": "Wazuh",
  "sourceRef": "semgrep-test-6",
  "type": "external",
  "tags": ["semgrep", "cicd", "wazuh", "security"]
}

change the old password in app.py since it wasn't taking the previous password and git isn't seeing any differenc

password = "super_secret_test_999_xyz" api_key = "test_api_key_12345"

None
None
we see this byt it didn't continue further to semgrep or webhook
None
after the correction and also makesure to add the webhook uri in yaml code you will get this output

Replace this part:

- name: Send alert to Shuffle webhook (only if issues found)
        run: |
          if grep -q '"results": \[\]' semgrep-report.json; then
            echo "No issues found, skipping alert"
          else
            curl -X POST "https://shuffler.io/api/v1/hooks/webhook_dd66026c-22ef-47db-acc4-d0eea74008cc" \
            -H "Content-Type: application/json" \
            -d "{\"title\":\"CI/CD Semgrep Alert\",\"description\":\"Semgrep security finding detected in CI/CD pipeline\",\"severity\":3,\"source\":\"GitHub Actions\",\"sourceRef\":\"semgrep-${GITHUB_RUN_ID}\",\"type\":\"external\",\"tags\":[\"semgrep\",\"cicd\",\"github-actions\"]}"
          fi

change this in shuffle which is for hive

{
  "title": "$exec.title",
  "description": "$exec.description",
  "severity": $exec.severity,
  "source": "$exec.source",
  "sourceRef": "$exec.sourceRef",
  "type": "$exec.type",
  "tags": $exec.tags
}
git commit --allow-empty -m "Trigger pipeline"
git push origin advanced-devsecops
None

You now have a FULL DevSecOps + SOC Automation pipeline:

Flow:

  1. Code pushed to GitHub
  2. CI/CD pipeline runs
  3. Security tools scan:
  • Gitleaks (secrets)
  • Semgrep (SAST)
  • Trivy (container scan)
  • pip-audit (dependencies)
  1. Vulnerability detected
  2. Webhook triggered
  3. Shuffle processes it
  4. Alert created in TheHive
  5. Email notification sent