Skip to content

Enhance log sniffing features with AI analysis and REST API - #30

Merged
vsilent merged 236 commits into
testingfrom
main
Nov 13, 2026
Merged

vsilent merged 236 commits into
testingfrom
main

Conversation

@vsilent

@vsilent vsilent commented Nov 13, 2026

Copy link
Copy Markdown

No description provided.

vsilent and others added 30 commits March 10, 2021 17:59
* upload artifacts

* upload artifacts

* syntax fix

* try another approach

* list files

Co-authored-by: vsilent <jabberroid@gmail.com>
Updates the requirements on [actix](https://github.com/actix/actix) to permit the latest version.
- [Release notes](https://github.com/actix/actix/releases)
- [Commits](actix/actix@actix-v0.11.0-beta.3...v0.11.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Updates the requirements on [actix-cors](https://github.com/actix/actix-extras) to permit the latest version.
- [Release notes](https://github.com/actix/actix-extras/releases)
- [Commits](actix/actix-extras@cors-v0.3.0...cors-v0.5.4)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Updates the requirements on [bcrypt](https://github.com/Keats/rust-bcrypt) to permit the latest version.
- [Release notes](https://github.com/Keats/rust-bcrypt/releases)
- [Commits](Keats/rust-bcrypt@v0.8.2...v0.9.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Updates the requirements on [env_logger](https://github.com/env-logger-rs/env_logger) to permit the latest version.
- [Release notes](https://github.com/env-logger-rs/env_logger/releases)
- [Changelog](https://github.com/env-logger-rs/env_logger/blob/master/CHANGELOG.md)
- [Commits](rust-cli/env_logger@v0.7.1...v0.8.3)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Updates the requirements on [bigdecimal](https://github.com/akubera/bigdecimal-rs) to permit the latest version.
- [Release notes](https://github.com/akubera/bigdecimal-rs/releases)
- [Commits](akubera/bigdecimal-rs@v0.0.14...v0.2.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Updates the requirements on [actix-service](https://github.com/actix/actix-net) to permit the latest version.
- [Release notes](https://github.com/actix/actix-net/releases)
- [Commits](actix/actix-net@service-v1.0.6...rt-v2.0.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [codacy/codacy-analysis-cli-action](https://github.com/codacy/codacy-analysis-cli-action) from 2.0.1 to 3.0.1.
- [Release notes](https://github.com/codacy/codacy-analysis-cli-action/releases)
- [Commits](codacy/codacy-analysis-cli-action@2.0.1...84fbefe)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [codacy/codacy-analysis-cli-action](https://github.com/codacy/codacy-analysis-cli-action) from 3.0.1 to 3.0.2.
- [Release notes](https://github.com/codacy/codacy-analysis-cli-action/releases)
- [Commits](codacy/codacy-analysis-cli-action@3.0.1...3.0.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/cache](https://github.com/actions/cache) from 2.1.4 to 2.1.5.
- [Release notes](https://github.com/actions/cache/releases)
- [Commits](actions/cache@v2.1.4...v2.1.5)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [codacy/codacy-analysis-cli-action](https://github.com/codacy/codacy-analysis-cli-action) from 3.0.2 to 3.0.3.
- [Release notes](https://github.com/codacy/codacy-analysis-cli-action/releases)
- [Commits](codacy/codacy-analysis-cli-action@3.0.2...3.0.3)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Updates the requirements on [actix](https://github.com/actix/actix) to permit the latest version.
- [Release notes](https://github.com/actix/actix/releases)
- [Commits](actix/actix@v0.11.0...v0.12.0)

---
updated-dependencies:
- dependency-name: actix
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/cache](https://github.com/actions/cache) from 2.1.5 to 2.1.6.
- [Release notes](https://github.com/actions/cache/releases)
- [Commits](actions/cache@v2.1.5...v2.1.6)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Updates the requirements on [bcrypt](https://github.com/Keats/rust-bcrypt) to permit the latest version.
- [Release notes](https://github.com/Keats/rust-bcrypt/releases)
- [Commits](Keats/rust-bcrypt@v0.9.0...v0.10.0)

---
updated-dependencies:
- dependency-name: bcrypt
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [codacy/codacy-analysis-cli-action](https://github.com/codacy/codacy-analysis-cli-action) from 3.0.3 to 4.0.0.
- [Release notes](https://github.com/codacy/codacy-analysis-cli-action/releases)
- [Commits](codacy/codacy-analysis-cli-action@3.0.3...4.0.0)

---
updated-dependencies:
- dependency-name: codacy/codacy-analysis-cli-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Updates the requirements on [env_logger](https://github.com/env-logger-rs/env_logger) to permit the latest version.
- [Release notes](https://github.com/env-logger-rs/env_logger/releases)
- [Changelog](https://github.com/env-logger-rs/env_logger/blob/main/CHANGELOG.md)
- [Commits](rust-cli/env_logger@v0.8.3...v0.9.0)

---
updated-dependencies:
- dependency-name: env_logger
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Updates the requirements on [bigdecimal](https://github.com/akubera/bigdecimal-rs) to permit the latest version.
- [Release notes](https://github.com/akubera/bigdecimal-rs/releases)
- [Commits](akubera/bigdecimal-rs@v0.2.0...v0.3.0)

---
updated-dependencies:
- dependency-name: bigdecimal
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Updates the requirements on [actix-tls](https://github.com/actix/actix-net) to permit the latest version.
- [Release notes](https://github.com/actix/actix-net/releases)
- [Commits](actix/actix-net@rt-v2.0.0...tls-v3.0.0)

---
updated-dependencies:
- dependency-name: actix-tls
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
VladimirSivac and others added 28 commits November 2, 2026 22:57
style(web): polish dashboard UI, responsive layout, and navigation
- /ai-built-startup-security: what early traffic looks like on a fresh
  server (scanners with the production ban evidence, brute force,
  PHP2Shell probes, webshell drops), a five-minute StackdogApp setup
  with real commands and settings, the detections that ship by default,
  honest limits, and a first-weeks checklist.
- Framed for founders shipping apps built with AI: the code arrives in
  days, the ops layer does not.
- Sitemap entry added; internal links to the quickstart, PHP shell
  detection, PHP2Shell check and fail2ban comparison pages.
docs(website): add a security article for AI-built startups
…unts

SSHole (CVE-2026-123158) is an OpenSSH authentication bypass on musl
with PAM disabled, Alpine's default server included: a 257-1024 byte
password makes the hash attempt fail, the failure reads as the same '*'
the account stores, and the login succeeds. Fixed in OpenSSH 10.6p2.

- vuln.sshole flags openssh-server builds older than 10.6p2 in the apk
  package database, on the host and in running containers: apk is the
  musl half of the vulnerable configuration. The PAM flavor and glibc
  distributions are not affected and are not reported. Alert only: a
  version names no attacker.
- ssh.password-login alerts on password logins to key-only accounts
  (root, alpine by default; STACKDOG_SSHOLE_KEYONLY_USERS extends the
  list) and feeds the ban engine: Alpine's own incident guidance is to
  hunt exactly these lines. One offense per line, repeats numbered so
  the registry counts them.
- STACKDOG_SSHOLE_ENABLED=false turns both off; the version check reads
  package databases through the same host-root mount as the PHP checks.

Docs: README subsection, CHANGELOG, .env.sample, and an
/am-i-affected style page at /sshole-check with an in-browser version
check (ssh -V plus the platform conditions), wired into sitemap and
footer.

Tests: 468+8+108 pass, clippy -D warnings and fmt clean, website build
15/15.
One password login to a key-only account is already a foothold: waiting
for the five-offense threshold hands the intruder time to switch the
monitoring off. OffenseInput gains an immediate flag, and
ssh.password-login sets it, so the first line bans the address. The gate
still runs first: allowlisted and protected addresses are never touched.

The exposure alerts now say what defends the box until the upgrade, so
the Slack message is actionable on arrival: vuln.sshole names the
first-line ban on password logins to key-only accounts, vuln.php2shell
names the out-of-memory bans, the webshell scan and session.upload_
progress.enabled = 0.

Tests: an end-to-end attack simulation on captured fixtures (Alpine
3.20's openssh 9.7_p1-r6 apk database and a real sshd login line from a
lab container) walks exposure finding, log findings and the ban to an
active block; a new test proves one line bans and the allowlist still
holds. 469+8+108 pass, clippy -D warnings and fmt clean, website 15/15.
The two SSHole ban tests unwrapped apply_ip_ban, which panics on a Linux
runner without root: the nftables backend fails with 'Operation not
permitted' before the block is recorded. Mirror the existing pattern in
test_apply_ip_ban_blocks_and_emits_alert_after_repeated_anomalies: on
Linux without root, accept that error and skip the block assertions; the
detection assertions still run everywhere. macOS keeps asserting the
ban end to end.
feat: detect SSHole exposure, ban password logins to key-only accounts, and say what defends the box until the upgrade
ci: post published releases to Discord via webhook
Two new alert channels, routed exactly like Slack (Critical, High,
Medium):

- Discord (STACKDOG_DISCORD_WEBHOOK_URL): plain content messages under
  the webhook's own name and avatar, truncated to Discord's 2000
  character limit on a UTF-8 boundary.
- Telegram (STACKDOG_TELEGRAM_BOT_TOKEN + STACKDOG_TELEGRAM_CHAT_ID):
  plain text through the Bot API sendMessage, truncated to the 4096
  character limit. No parse_mode, so alert content can never break the
  request by looking like markup. Both values are required; a partial
  config does not enable the channel.

Telegram is alerts only: action notifications (bans, quarantines) are
deliberately routed away from it, and the channel list documents that.

Revives tests/alerting/, which tests/integration.rs never included: four
files of tests never compiled or ran. Added mod alerting;, fixed the one
compile error (async send without await), and cleaned the warnings the
dead code had accumulated.

Tests: 470+8+155 pass (was 469+8+108; +3 Discord, +3 Telegram integration
tests, +1 unit test for the partial Telegram config, +44 revived).
clippy -D warnings and fmt clean.
- repository pointed at vsilent/stackdog; the repo lives at
  trydirect/stackdog. Adds homepage and documentation (stackdog.app).
- exclude web/, website/ and ebpf/ from the package: the workspace root
  is the repo root, and the published artifact is the server binary, not
  two frontend trees and a sibling crate.
Configuration resolution, nearest first: real environment variables,
then ./.env in the working directory, then ~/.config/stackdog/.env
($XDG_CONFIG_HOME/stackdog/.env when set). dotenvy never overrides an
already-set variable, so the nearest file wins. A missing file is no
longer a warning: every setting has a default.

stackdog init writes the starter config to the config directory and
never overwrites an existing file, since it holds credentials. The
template is the repo's .env.sample, embedded in the binary so it works
for cargo install and brew, where no sample file sits next to the
binary.

Verified by hand against a throwaway HOME: init creates the file, a
second run reports it exists and leaves it alone. 469+8+108 tests pass,
clippy -D warnings and fmt clean.
feat: add Discord and Telegram notification channels
chore: fix crates.io metadata and trim the package
# Conflicts:
#	CHANGELOG.md

Co-authored-by: vsilent <42473+vsilent@users.noreply.github.com>
feat: read config from ~/.config/stackdog and add stackdog init
Two shapes in main.rs the runner's rustfmt formats differently: the
HOME fallback closure stays broken across lines, and the closing
println fits on one line. Verified with cargo fmt --check exit codes
rather than output tails this time.
Two shapes in main.rs the runner's rustfmt formats differently: the
HOME fallback closure stays broken across lines, and the closing
println fits on one line. Verified with cargo fmt --check exit codes
rather than output tails this time.
style: match the format check's rustfmt output
style: match the format check's rustfmt output
vuln.cve-watch closes the gap the two CVE-specific detectors leave: a
CVE published tonight is checked on the next refresh without a Stackdog
release. Every package on the host and in running containers is matched
against OSV, and results are cross-referenced with CISA's KEV catalog.

Feed work, all verified against the live APIs while building:
- OSV matches Debian and Ubuntu versions server-side (the full deb
  version, epoch included); Alpine versions must be sent without the
  -rN package revision, which is what their data carries. querybatch
  does one request per refresh instead of one per package.
- CISA's canonical KEV URL rejects datacenter clients (403), so the
  cisagov/kev-data mirror is tried first. KEV entries are Critical;
  everything else carries the advisory's own severity label.
- Answers are cached in SQLite for 24h (surviving restarts), and a
  match is alerted once per owner, package and CVE.

The blocking HTTP runs on a blocking thread via a process-wide monitor:
reqwest::blocking panics when its runtime is dropped inside an async
context, which is how the sniff loop calls this. KEV is fetched only
when something matched.

Findings are alert-only (vuln.cve-watch joins the never-ban list): a
version names no attacker. Alerts say to check the vendor's advisory —
distributions backport fixes, and config-dependent CVEs do not survive
being flattened into a version range. 50 findings per pass, KEV first.

Tests: 477+8+155 pass (7 new: apk revision stripping, os-release to
ecosystem mapping, CVE-alias preference, KEV severity, batch alignment,
inventory under a root, reported-never-again), plus an opt-in live test
against the real OSV API. clippy -D warnings and fmt clean.
feat: match the package inventory against public CVE feeds
Comment on lines +68 to +69
" Created execve event: PID={}, UID={}",
execve_event.pid, execve_event.uid
Comment on lines +82 to +83
" Created connect event: PID={}, Command={:?}",
connect_event.pid, connect_event.comm
Comment thread src/sniff/analyzer.rs
Comment on lines +598 to +600
"Using API key: {}...{}",
&key[..key.len().min(4)],
&key[key.len().saturating_sub(4)..]
Comment on lines +11 to +75
name: Build & Test
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Install stable toolchain
uses: actions-rs/toolchain@v1
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
profile: minimal
override: true
components: rustfmt, clippy
targets: x86_64-unknown-linux-musl

- name: Cache cargo registry
uses: actions/cache@v2.1.4
with:
path: ~/.cargo/registry
key: docker-registry-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
docker-registry-
docker-

- name: Cache cargo index
uses: actions/cache@v2.1.4
with:
path: ~/.cargo/git
key: docker-index-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
docker-index-
docker-
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2

- name: Install cross
run: cargo install cross --git https://github.com/cross-rs/cross

- name: Generate Secret Key
run: |
head -c16 /dev/urandom > src/secret.key
run: head -c16 /dev/urandom > src/secret.key

- name: Cache cargo build
uses: actions/cache@v2.1.4
with:
path: target
key: docker-build-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
docker-build-
docker-

- name: Cargo check
uses: actions-rs/cargo@v1
with:
command: check
- name: Check
run: cargo check

- name: Cargo test
if: ${{ always() }}
uses: actions-rs/cargo@v1
with:
command: test
- name: Format check
run: cargo fmt --all -- --check

- name: Rustfmt
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
components: rustfmt
command: fmt
args: --all -- --check

- name: Rustfmt
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
components: clippy
command: clippy
args: -- -D warnings

- name: Run cargo build
uses: actions-rs/cargo@v1
with:
command: build
args: --release
- name: Clippy
run: cargo clippy -- -D warnings

- name: Test
run: cargo test

- name: Build static release
env:
CARGO_TARGET_DIR: target-cross
STACKDOG_GIT_HASH: ${{ github.sha }}
run: cross build --release --target x86_64-unknown-linux-musl

- name: npm install, build, and test
- name: Build frontend
working-directory: ./web
run: |
npm install
if [ -f package-lock.json ]; then
npm ci
else
npm install
fi
npm run build
# npm test

- name: Archive production artifacts
uses: actions/upload-artifact@v2
with:
name: dist-without-markdown
path: |
web/dist
!web/dist/**/*.md

# - name: Archive code coverage results
# uses: actions/upload-artifact@v2
# with:
# name: code-coverage-report
# path: output/test/code-coverage.html
- name: Display structure of downloaded files
run: ls -R web/dist

- name: Copy app files and zip
- name: Package app
run: |
mkdir -p app/stackdog/dist
cp target/release/stackdog app/stackdog
cp -a web/dist/. app/stackdog
cp target-cross/x86_64-unknown-linux-musl/release/stackdog app/stackdog/
cp -a web/dist/. app/stackdog/
cp docker/prod/Dockerfile app/Dockerfile
cd app
touch .env
tar -czvf ../app.tar.gz .
cd ..
touch app/.env
tar -czf app.tar.gz -C app .

- name: Upload app archive for Docker job
uses: actions/upload-artifact@v2.2.2
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: artifact-linux-docker
name: app-archive
path: app.tar.gz
retention-days: 1

cicd-docker:
name: CICD Docker
docker:
Comment on lines +76 to +106
name: Docker Build & Push
runs-on: ubuntu-latest
needs: cicd-linux-docker
needs: build
steps:
- name: Download app archive
uses: actions/download-artifact@v2
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: app-archive

- name: Extract archive
run: tar -xzf app.tar.gz

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
name: artifact-linux-docker
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Extract app archive
run: tar -zxvf app.tar.gz
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
trydirect/stackdog:latest
trydirect/stackdog:${{ github.sha }}

docker-ui:
Comment on lines +107 to +134
name: Dashboard Image Build & Push
runs-on: ubuntu-latest
# Independent of the Rust build: the dashboard is its own image, and there
# is no reason to wait on a cross-compile to publish it.
steps:
- uses: actions/checkout@v4

- name: Display structure of downloaded files
run: ls -R
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Docker build and publish
uses: docker/build-push-action@v1
- name: Login to Docker Hub
if: github.event_name == 'push'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: trydirect/stackdog
add_git_labels: true
tag_with_ref: true
#no-cache: true

# No build args: the image is generic and reads its API address from the
# environment at container start.
- name: Build and push dashboard
uses: docker/build-push-action@v6
with:
context: .
file: docker/ui/Dockerfile
push: ${{ github.event_name == 'push' }}
tags: |
trydirect/stackdog-ui:latest
trydirect/stackdog-ui:${{ github.sha }}
@vsilent
vsilent merged commit a2a84c8 into testing Nov 13, 2026
7 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants