SuperAPI AI Directory

dym-cti-expert-ci-monitoring

Security & auditing

View repository

Updated 10-18 01:24

About this skill

Skill: dym-cti-expert-ci-monitoring — Run cti-expert in the CI of your project

Why use it: turn the free, keyless ops into a periodic monitoring job — brand typosquats, new certs in CT log — instead of remembering to run it by hand. What it generates: a workflow in your repo of yours, a committed state file, and JSON artifacts to inspect when there's a finding.

The example below was written fresh for the consumer's case. It is not a copy of .github/workflows/audit.yml or smoke.yml in the cti-expert repo — those two files gate development of the tool itself (structural drift, install from a clean machine) and do nothing for you. Read them to understand, don't copy.


Which op to choose for CI

Conditions for an op to suit CI: no key needed, no .venv, and the exit code says something meaningful. Three candidates:

Op Does what Cost
impersonate <domain> generate + check typosquat variants via crt.sh + DNS free (adding --fofa/--urlscan costs credit)
ct-monitor watch --state <file> <pattern> poll CT log, filter new certs matching the brand free
hash-id, sensitive-paths, email-hygiene pure, no network free

Offline check before wiring (real run, no network access):

python3 scripts/backend/intel.py impersonate example.com --generate-only --max 5
{"seed":"example.com","label":"example","tld":"com","variants":[
 {"domain":"xample.com","technique":"omission"},
 {"domain":"eeexample.com","technique":"repetition"},
 {"domain":"wxample.com","technique":"replacement"},
 {"domain":"wexample.com","technique":"insertion"},
 {"domain":"rxample.com","technique":"replacement"}],
 "count":5,"truncated":true}

--generate-only is a fully offline mode — use it for the "can CI run it" step without depending on the network.

Minimal example — GitHub Actions

Place in your repo of yours, .github/workflows/brand-watch.yml:

name: brand-watch

on:
  schedule:
    - cron: '0 6 * * *'      # 06:00 UTC mỗi ngày
  workflow_dispatch:

permissions:
  contents: write            # để commit lại file state của ct-monitor

jobs:
  watch:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

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

      # Lớp stdlib của cti-expert không cần cài gì — clone là chạy.
      - name: Fetch cti-expert
        run: git clone --depth 1 https://github.com/7onez/cti-expert.git "$RUNNER_TEMP/cti"

      - name: Typosquat sweep
        run: |
          set -euo pipefail
          python3 "$RUNNER_TEMP/cti/scripts/backend/intel.py" \
            impersonate "${{ vars.BRAND_DOMAIN }}" --pretty > impersonate.json

      - name: CT-log watch (state được commit để chỉ báo cái MỚI)
        run: |
          set -euo pipefail
          mkdir -p .brandwatch
          python3 "$RUNNER_TEMP/cti/scripts/backend/intel.py" \
            ct-monitor watch "${{ vars.BRAND_KEYWORD }}" \
            --state .brandwatch/ct-state.json --json > ct-new.json

      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: brand-watch-${{ github.run_number }}
          path: |
            impersonate.json
            ct-new.json

      - name: Persist CT state
        run: |
          git config user.name  github-actions
          git config user.email github-actions@github.com
          git add .brandwatch/ct-state.json
          git diff --cached --quiet || git commit -m "chore(brand-watch): cập nhật state CT"
          git push

Two intentional details:

  • --state is committed back. Without it, every run treats every cert as new and the job becomes a spam generator.
  • No key in the workflow. Both ops run keyless. The day you add --fofa/--urlscan, that's when you need a secret — and then remember to reread 02 about the two cost ledgers.

Branch on exit code — read 04 first

Don't write || echo "failed". The dispatcher's exit codes have specific meanings:

set +e
python3 "$CTI/scripts/backend/intel.py" impersonate "$DOMAIN" --pretty > out.json
rc=$?
set -e
case "$rc" in
  0) : ;;                                            # chạy được — đọc out.json để quyết định
  2) echo "::error::sai tên op — chạy intel.py list"; exit 1 ;;
  3) echo "::error::không thấy backend — clone hỏng hoặc thiếu intel_engine"; exit 1 ;;
  4) echo "::error::component chưa cài cho op này"; exit 1 ;;
  5) echo "::error::interpreter không exec được — đặt INTEL_PY"; exit 1 ;;
  *) echo "::error::script engine thoát $rc"; exit 1 ;;
esac

Important: 0 means the op finished running, not means no findings. The verdict is in the JSON, not in the exit code. To gate, read the JSON yourself:

python3 - <<'PY'
import json
d = json.load(open("impersonate.json"))
imp = d["artifacts"]["impersonation"]
print(f'::notice::sinh {imp["generated"]} bien the | '
      f'{imp["existing_count"]} da TON TAI | {imp["candidate_count"]} chua dang ky')

confirmed = [p for p in d["pivots"] if p.get("kind") == "impersonation:candidate"]
if confirmed:
    print(f"::warning::{len(confirmed)} lookalike da ton tai")
    for p in confirmed[:20]:
        print(" -", p.get("value"), "|", p.get("confidence"))
PY

The fields used above (artifacts.impersonation.generated / existing_count / candidate_count, and pivots[].kind) come from build_impersonation_result in intel_engine/WebPivot/tools/wp_impersonate.py, not guessing. Important distinction: existing = has DNS resolution or seen in CT/FOFA/urlscan; candidate = generated variants with no evidence yet — that is a watchlist to monitor, not confirmed infrastructure.

What should not go into CI

  • Any op in the outbound gate list (07): engage, engage-harvest, --submit, misp push/publish. Hook actionguard.py does not run in CI — it is a Claude Code hook, not a shell one. In CI you only have the gate in the code, and with misp publish there is no undo button at all.
  • Full case pipeline (pipeline open) with --serp / --whois-reverse / --fofa-full: every run burns real credits, and cron runs forever.
  • Anything that writes to intel_engine/cases/ then commits — that is investigation data, and RULE 1 exists precisely because of this.