Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: Project website and tool index
url: https://lidless.dev/soc-stack
about: Overview, the wider lidless.dev SOC tooling, and where this fits.
- name: Security report (do not file publicly)
url: https://github.com/solomonneas/soc-stack/security/advisories/new
about: Report a vulnerability privately. See SECURITY.md. Do not open a public issue for anything that leaks a secret.
- name: Question or usage help
url: https://github.com/solomonneas/soc-stack/discussions
about: For setup questions and general usage, start a discussion rather than filing a bug.
1 change: 1 addition & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- [ ] Integration assertion passes on the self-hosted Proxmox runner (if touching a component or lib)
- [ ] No em-dashes in commit messages or files
- [ ] No `Co-Authored-By` trailers
- [ ] No leaked PII, secrets, real private IPs, hostnames, or `/home/<user>` paths (documentation IPs use the RFC 5737 range; real values are scrubbed)

## Related
<!-- Closes #N, refs #N, or "n/a" -->
19 changes: 14 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

# Least privilege: this workflow only reads the repo. No job needs write scopes.
permissions:
contents: read

jobs:
shellcheck:
name: Shellcheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Install shellcheck
run: sudo apt-get update -qq && sudo apt-get install -y -qq shellcheck
- name: Shellcheck all bash
Expand All @@ -31,7 +35,7 @@ jobs:
name: Bats unit tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
submodules: recursive
- name: Install jq
Expand All @@ -43,7 +47,7 @@ jobs:
name: Component manifest validation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Install jq
run: sudo apt-get install -y -qq jq
- name: Validate every component manifest is parseable JSON (after stripping comments)
Expand All @@ -58,6 +62,11 @@ jobs:
integration-component:
name: Integration (per-component)
needs: [shellcheck, unit-tests, manifest-schema]
# Never run untrusted fork-PR code on the self-hosted Proxmox runner: that
# runner can create/destroy LXCs and its account has broad sudo on the host.
# Only same-repo PRs (and pushes to main) reach it; fork PRs run just the
# sandboxed ubuntu-latest jobs above.
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: [self-hosted, soc-stack]
concurrency:
group: soc-stack-integration
Expand All @@ -75,7 +84,7 @@ jobs:
- zeek-suricata
- mcp
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Deploy + assert ${{ matrix.component }}
run: bash tests/integration/ci-helpers/boot-test-env.sh "${{ matrix.component }}"
- name: Tear down
Expand All @@ -91,7 +100,7 @@ jobs:
group: soc-stack-integration
cancel-in-progress: false
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Deploy full stack + 5 cross-component integrations
run: bash tests/integration/ci-helpers/boot-test-env.sh all
- name: Tear down
Expand Down
88 changes: 88 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,91 @@ docs/design/plans/
# Local brigade memory (cards are local-only, never committed)
/memory/
.brigade/

# >>> brigade gitignore block >>>
# Managed by `brigade init`. Edit between the markers to customize.
# Re-running `brigade init` replaces only the content between markers.

# claude: handoffs are session-local and may contain private context.
.claude/memory-handoffs/*
!.claude/memory-handoffs/TEMPLATE.md
!.claude/memory-handoffs/.gitkeep

# Daily session logs are machine-local raw context.
memory/20[0-9][0-9]-[0-1][0-9]-[0-3][0-9].md

# Review inbox: ambiguous handoffs awaiting human triage.
memory/handoff-inbox/

# brigade local state (logs, scrub cache, dogfood runs, work sessions).
.brigade/
.brigade/backups/
.brigade/backups.toml
.brigade/center/
.brigade/context/
.brigade/dogfood.toml
.brigade/handoffs/
.brigade/handoff-sources.json
.brigade/learn/
.brigade/projects.toml
.brigade/release/
.brigade/repos.toml
.brigade/chat-surfaces.toml
.brigade/daily.toml
.brigade/memory-care.toml
.brigade/reviews.toml
.brigade/scanners.toml
.brigade/security.toml
.brigade/tools.toml
.brigade/logs/
.brigade/runs/
.brigade/scrub-cache/
.brigade/scanners/
.brigade/security/
.brigade/tools/
.brigade/chat-memory-sweeps/
.brigade/work/
.brigade/mcp/
# .brigade/mcp.json is the shared canonical MCP server catalog: keep it tracked.
!.brigade/mcp.json

# Generated tool projections are local harness state.
.claude/commands/
.codex/skills/
.opencode/commands/
.opencode/superpowers/
.antigravity/commands/
.antigravity/superpowers/
.pi/commands/
.pi/superpowers/
.cursor/rules/
.cursor/skills/
.aider/commands/
.aider/skills/
.goose/commands/
.goose/skills/
.continue/rules/
.continue/skills/
.copilot/instructions/
.copilot/skills/
.qwen/commands/
.qwen/skills/
.kimi/commands/
.kimi/skills/
.adal/commands/
.adal/skills/
.openhands/instructions/
.openhands/skills/
.grok/instructions/
.grok/skills/
.amp/instructions/
.amp/skills/
.crush/instructions/
.crush/skills/
.hermes/commands/
.hermes/superpowers/
.openclaw/commands/
.openclaw/superpowers/
.mcp/
scripts/*.md
# <<< brigade gitignore block <<<
27 changes: 26 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,38 @@ All notable changes to soc-stack are documented in this file. Format follows [Ke
## [Unreleased]

### Added
- README adoption pass: prominent website link, live CI and release badges, a keyword-rich "What it does" section, a redacted result-JSON example block, and "Why not something else?" / "What soc-stack is not" sections
- `CODE_OF_CONDUCT.md` (Contributor Covenant 2.1)
- `.github/ISSUE_TEMPLATE/config.yml` (disables blank issues, routes security reports and questions off the issue tracker)
- No-PII / no-secret-leak checkbox in the pull request template
- TTY component picker when run locally without `--components`/`--manifest`
- `--include-secrets-json` (result JSON redacts credentials by default) and `--mcp-bind-host` (MCP SSE binds 127.0.0.1 by default)
- `SECURITY.md` documenting the threat model and hardening posture
- `docs/adding-a-component.md` (replaces the stale `docs/adding-a-stack.md`)
- Bats coverage for the exit-code contract, integration state tracking, and IP allocation bounds (105 unit tests)
- Bats coverage for the exit-code contract, integration state tracking, and IP allocation bounds, plus the hardening pass (state-file safety, redaction, static/VLAN networking, flag parsing, MCP exposure warning): 123 unit tests
- `--gateway` flag to set the default route for static-mode containers

### Security
- MCP SSE endpoints now enforce their bearer token. `mcp-proxy` has no auth of its own, so each endpoint is fronted by an nginx gateway that returns `401` unless the `Authorization` header is the exact per-server token; `mcp-proxy` binds loopback behind it. Previously the token was advertised to clients but never enforced
- Credentials are no longer passed on a process command line during provisioning (`/proc/<pid>/cmdline` exposure): the LXC root password is set via `chpasswd` over stdin, and the TheHive/Cortex admin-credential API calls use `curl --data @-`
- CI: the self-hosted Proxmox integration jobs are gated to same-repo PRs and pushes so fork-PR code cannot run as root on the host; `actions/checkout` pinned to a commit SHA; workflow runs with `permissions: contents: read`
- Result-JSON redaction broadened to more key names (`pwd`, `passwd`, `bearer`, `credential`, `private_key`) and to credentials embedded in URL values
- Cortex admin password hash uses a full-length random salt (was a 24-bit, fixed-prefix salt)
- MCP integration parses `rapid7.env`/`sophos.env` as data instead of sourcing them as shell
- The installer warns (in logs and the result JSON) when MCP binds a non-loopback host

### Removed
- Dead legacy installer tree `scripts/setup/**` (superseded by the `scripts/install.sh` orchestrator and `scripts/components/*`). It was excluded from the shellcheck gate and carried `verify=False` TLS-off alert forwarding, a MISP installer fetched from a mutable branch and run as root, cleartext password printing, and an allow-all firewall

### Fixed
- `gen_password` returned exit 141 on success under `set -o pipefail` (SIGPIPE from `tr`), a latent abort for any `set -e` caller
- `state_set` overwrote a good state file with empty content when `jq` failed (only replaces on success now); corrupt state files are tolerated instead of aborting the run, and the temp file is written in the target directory for an atomic rename
- `--ip-mode static` now sets a default route (new `--gateway` flag, else the first host of the range); static containers previously came up with no route and failed the network wait
- `--vlan` is now applied to the container network config; it was validated and stored but never took effect
- Static IPs are allocated by the component's canonical ordinal, so component subsets and re-runs no longer collide on the same address
- A successful deploy records `status=deployed` authoritatively, so it is not re-deployed on every re-run when the in-LXC state file did not survive the pull
- `--flag=value` argument form is accepted (was rejected as an unknown flag)
- `validate_manifest` exact-matches component names (`grep -qw` had accepted `cortex` and treated names as regexes)
- Exit-code contract: integration failures now produce exit 4/5 as documented; `integration.status` is tracked per component
- `allocate_ip` bounds-checks the last octet instead of emitting invalid addresses
- TheHive/MISP default-credential rotation is verified before a component reports deployed; idempotent re-runs refuse to report deployed with missing credentials
Expand Down
19 changes: 19 additions & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Code of Conduct

This project follows the [Contributor Covenant, version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).

The full text lives at the link above. The short version: be respectful, assume good faith, keep feedback technical, and remember that everyone here is volunteering their time.

## Scope

The Code of Conduct applies to all project spaces: GitHub issues and pull requests on this repo, project-related discussions on Discord or other chat surfaces, and any in-person events where someone is representing the project.

## Reporting

If you experience or witness a violation, please email **me@solomonneas.dev**. Reports are confidential. You should get an acknowledgment within 72 hours. <!-- content-guard: allow pii/email -->

The maintainer is also the recipient of security reports, see [SECURITY.md](SECURITY.md). Please tag the subject line so it is easy to route (for example "CoC report" vs "security report").

## Enforcement

The maintainer is responsible for clarifying and enforcing standards. Enforcement actions can include private warnings, public clarifications, temporary or permanent bans from project spaces, and reverting or rejecting contributions.
Loading