Repository navigation
Conversation
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>
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
chore: bump version to v0.3.0
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 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 }} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.