Skip to content
Merged
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ configs
frontend/node_modules
frontend/dist
gensui
!gensui/
!gensui/**
gensui/.env
gensui/certs
gensui/data
gensui/logs
gensui/frontend/node_modules
gensui/frontend/dist
tests
certs
*.db
Expand Down
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ QDRANT_PATH=./data/qdrant
# Security
SECRET_KEY=change-me-to-a-random-64-char-string
VAULT_ENCRYPTION_KEY=change-me-to-a-fernet-base64-key
SHOGUN_INFRASTRUCTURE_ADMIN_TOKEN=

# Outbound A2A/Gensui destination policy
A2A_DESTINATION_POLICY=private_allowed
GENSUI_DESTINATION_POLICY=loopback_allowed
OUTBOUND_ALLOWLIST=
ALLOW_HTTP_ON_PRIVATE_NETWORK=true
ALLOW_HTTP_ON_PUBLIC_NETWORK=false
A2A_ALLOWED_PORTS=
GENSUI_ALLOWED_PORTS=

# Storage
VAULT_PATH=./vault
Expand Down
10 changes: 10 additions & 0 deletions .env.server.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ POSTGRES_PASSWORD=change-me-postgres-password

SECRET_KEY=change-me-to-a-random-64-char-string
VAULT_ENCRYPTION_KEY=change-me-to-an-independent-random-64-char-string
SHOGUN_INFRASTRUCTURE_ADMIN_TOKEN=change-me-to-an-independent-infrastructure-admin-token

# Outbound federation policy. Metadata and link-local addresses are always blocked.
A2A_DESTINATION_POLICY=private_allowed
GENSUI_DESTINATION_POLICY=private_allowed
OUTBOUND_ALLOWLIST=
ALLOW_HTTP_ON_PRIVATE_NETWORK=true
ALLOW_HTTP_ON_PUBLIC_NETWORK=false
A2A_ALLOWED_PORTS=
GENSUI_ALLOWED_PORTS=

# Optional communication channels. They can also be configured in The Tenshu.
TELEGRAM_BOT_TOKEN=
Expand Down
18 changes: 18 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
version: 2
updates:
- package-ecosystem: npm
directory: /frontend
schedule:
interval: weekly
- package-ecosystem: npm
directory: /gensui/frontend
schedule:
interval: weekly
- package-ecosystem: pip
directory: /
schedule:
interval: weekly
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
27 changes: 27 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: CodeQL

on:
pull_request:
push:
branches: [main]
schedule:
- cron: "41 3 1 * *"
workflow_dispatch:

permissions:
contents: read
security-events: write

jobs:
analyze:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [python, javascript-typescript]
steps:
- uses: actions/checkout@v6
- uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
- uses: github/codeql-action/analyze@v4
194 changes: 194 additions & 0 deletions .github/workflows/security-hardening.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
name: Security hardening

on:
pull_request:
push:
branches: [main]
schedule:
- cron: "23 4 * * 1"
workflow_dispatch:

permissions:
contents: read

jobs:
python:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: pip
- name: Install CPU runtime and development dependencies
run: |
python -m pip install --upgrade pip
python -m pip install --index-url https://download.pytorch.org/whl/cpu "torch>=2.4.0"
python -m pip install -e ".[dev,office]"
- name: Import and lint
run: |
python -c "import shogun.app; import gensui.app"
# Full-tree lint has substantial pre-existing debt. Gate the security
# boundary and deployment-path files introduced by this remediation.
python -m ruff check \
shogun/api/a2a.py \
shogun/api/gensui_config.py \
shogun/api/infrastructure_auth.py \
shogun/config.py \
shogun/integrations/a2a_client.py \
shogun/services/ssrf_guard.py \
gensui/app.py \
gensui/config.py \
tests/test_ssrf_guard.py \
tests/test_gensui_frontend.py
- name: Security regression tests
# The repository-wide suite has unrelated pre-existing failures. Keep
# this required gate scoped to the remediation's 39 regression cases.
run: python -m pytest -q tests/test_ssrf_guard.py tests/test_gensui_frontend.py

frontends:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- name: Tenshu
directory: frontend
lockfile: frontend/package-lock.json
- name: Gensui
directory: gensui/frontend
lockfile: gensui/frontend/package-lock.json
name: Frontend - ${{ matrix.name }}
defaults:
run:
working-directory: ${{ matrix.directory }}
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: "22"
cache: npm
cache-dependency-path: ${{ matrix.lockfile }}
package-manager-cache: true
- run: npm ci
- name: High/Critical dependency gate
run: npm run audit:security
# The repository has pre-existing full-tree lint debt. Keep the complete
# lint command available while gating newly added security boundary code.
- run: npm run lint:security
- run: npm run build
- name: XSS regression tests
if: matrix.directory == 'frontend'
run: npm run test:security

repository-security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Trivy repository, secret, and misconfiguration scan
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1
with:
scan-type: fs
scan-ref: .
scanners: vuln,secret,misconfig
severity: HIGH,CRITICAL
ignore-unfixed: true
trivyignores: .trivyignore.yaml
exit-code: "1"

gensui-container:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
- name: Build Gensui
run: docker build --file gensui/Dockerfile --tag shogun-gensui:ci .
- name: Scan Gensui image
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1
with:
image-ref: shogun-gensui:ci
severity: HIGH,CRITICAL
ignore-unfixed: true
exit-code: "1"
- name: Run hardened Gensui smoke test
run: |
docker volume create gensui-ci-data
docker volume create gensui-ci-logs
docker run -d --name gensui-ci \
--read-only --tmpfs /tmp:rw,noexec,nosuid,size=256m \
--cap-drop ALL --security-opt no-new-privileges:true \
-e GENSUI_JWT_SECRET=ci-only-random-jwt-secret-0123456789 \
-e GENSUI_ADMIN_PASSWORD=ci-only-random-admin-password \
-p 127.0.0.1:8787:8787 \
-v gensui-ci-data:/app/data -v gensui-ci-logs:/app/logs \
shogun-gensui:ci
for attempt in $(seq 1 60); do
curl --fail --silent http://127.0.0.1:8787/api/gensui/health && break
sleep 2
done
curl --fail --silent http://127.0.0.1:8787/
curl --fail --silent http://127.0.0.1:8787/agents
login_json="$(curl --fail --silent \
-H 'Content-Type: application/json' \
-d '{"email":"admin@gensui.local","password":"ci-only-random-admin-password"}' \
http://127.0.0.1:8787/api/gensui/auth/login)"
admin_token="$(python -c 'import json,sys; print(json.load(sys.stdin)["token"])' <<<"$login_json")"
curl --fail --silent \
-H "Authorization: Bearer $admin_token" \
http://127.0.0.1:8787/api/gensui/dashboard
test "$(docker exec gensui-ci whoami)" = "gensui"
docker exec gensui-ci sh -c "grep -R '/api/gensui' /app/frontend/dist/assets >/dev/null"
if docker exec gensui-ci sh -c "grep -R '/api/v1' /app/frontend/dist/assets >/dev/null"; then
echo "Gensui bundle unexpectedly references the Tenshu API prefix" >&2
exit 1
fi
- name: Gensui logs
if: failure()
run: docker logs gensui-ci || true
- name: Clean up Gensui
if: always()
run: |
docker rm -f gensui-ci || true
docker volume rm gensui-ci-data gensui-ci-logs || true

shogun-server-container:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v6
- name: Build Shogun Server
run: docker build --tag shogun-server:ci .
- name: Prepare Shogun Server environment
run: |
cp .env.server.example .env.server
sed -i 's/change-me-postgres-password/ci-postgres-password/' .env.server
sed -i 's/change-me-to-a-random-64-char-string/ci-application-secret-0123456789/' .env.server
sed -i 's/change-me-to-an-independent-random-64-char-string/ci-vault-secret-0123456789/' .env.server
sed -i 's/change-me-to-an-independent-infrastructure-admin-token/ci-infrastructure-token-0123456789/' .env.server
- name: Scan Shogun image
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1
with:
image-ref: shogun-server:ci
severity: HIGH,CRITICAL
ignore-unfixed: true
exit-code: "1"
- name: Start Shogun Server profile
run: |
docker tag shogun-server:ci shogun-server:local
docker compose --env-file .env.server -f docker-compose.server.yml up -d --no-build
for attempt in $(seq 1 120); do
curl --fail --silent http://127.0.0.1:8000/api/v1/health && break
sleep 2
done
curl --fail --silent http://127.0.0.1:8000/
curl --fail --silent http://127.0.0.1:8000/setup
test "$(docker exec shogun-server whoami)" = "shogun"
- name: Launch Mado Chromium as the runtime user
run: |
docker exec shogun-server python -c "import asyncio; from playwright.async_api import async_playwright; exec('async def smoke():\n async with async_playwright() as p:\n browser = await p.chromium.launch(headless=True)\n page = await browser.new_page()\n await page.set_content(\"<title>mado-smoke</title>\")\n assert await page.title() == \"mado-smoke\"\n await browser.close()\nasyncio.run(smoke())')"
- name: Shogun Server logs
if: failure()
run: docker compose --env-file .env.server -f docker-compose.server.yml logs
- name: Clean up Shogun Server
if: always()
run: docker compose --env-file .env.server -f docker-compose.server.yml down --volumes
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ qdrant_data/
# ── Local-only assets ────────────────────────────────────────
Blueprints and Requirement specs/
Assets/
!frontend/public/assets/
!frontend/public/assets/**
docker-compose.yml

scratch/
Expand Down
1 change: 1 addition & 0 deletions .node-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22
10 changes: 10 additions & 0 deletions .trivyignore.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
vulnerabilities:
- id: GHSA-qwww-vcr4-c8h2
paths:
- "frontend/package-lock.json"
- "gensui/frontend/package-lock.json"
expired_at: 2026-08-31
statement: >-
The advisory affects unstable React Server Components APIs, which these
Vite SPAs do not use. No patched react-router-dom 7.x release exists;
reassess the React Router 8 migration before this exception expires.
10 changes: 8 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ COPY shogun/ ./shogun/
COPY migrations/ ./migrations/

RUN pip install --no-cache-dir --index-url https://download.pytorch.org/whl/cpu "torch>=2.4.0" \
&& pip install --no-cache-dir ".[postgres,office]" \
&& pip install --no-cache-dir ".[server]" \
&& pip install --no-cache-dir --upgrade \
"jaraco.context>=6.1.0" \
"setuptools>=78.1.1" \
"wheel>=0.46.2" \
&& python -m playwright install --with-deps chromium \
&& rm -rf /var/lib/apt/lists/* /root/.cache

Expand All @@ -43,7 +47,9 @@ RUN groupadd --gid 10001 shogun \
&& mkdir -p /app/data /app/vault /app/logs /app/configs /app/tmp \
&& touch /app/.env \
&& chmod 0755 /usr/local/bin/shogun-entrypoint \
&& chown -R shogun:shogun /app /ms-playwright
&& chown -R shogun:shogun \
/app/data /app/vault /app/logs /app/configs /app/tmp /app/.env \
&& chmod -R a+rX /ms-playwright

USER shogun

Expand Down
8 changes: 5 additions & 3 deletions Gensui-Docker-Install.bat
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,11 @@ cd /d "%INSTALL_DIR%"
if not exist ".env" (
copy ".env.example" ".env" >nul 2>&1
:: Generate random JWT secret using PowerShell
for /f "usebackq tokens=*" %%i in (`powershell -NoProfile -Command "[Convert]::ToBase64String([Guid]::NewGuid().ToByteArray() + [Guid]::NewGuid().ToByteArray() + [Guid]::NewGuid().ToByteArray()).Replace('=', '').Replace('+', '').Replace('/', '')"`) do set JWT_SECRET=%%i
powershell -NoProfile -Command "(Get-Content .env) -replace 'change-me-to-a-random-64-char-string', '%JWT_SECRET%' | Set-Content .env"
echo [OK] .env created with secure random JWT secret.
for /f "usebackq tokens=*" %%i in (`powershell -NoProfile -Command "$b=New-Object byte[] 48; [Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($b); [Convert]::ToBase64String($b).Replace('=', '').Replace('+', '').Replace('/', '')"`) do set JWT_SECRET=%%i
for /f "usebackq tokens=*" %%i in (`powershell -NoProfile -Command "$b=New-Object byte[] 32; [Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($b); [Convert]::ToBase64String($b).Replace('=', '').Replace('+', '').Replace('/', '')"`) do set ADMIN_SECRET=%%i
powershell -NoProfile -Command "$c=Get-Content -Raw .env; $c=$c.Replace('change-me-to-a-random-64-char-string','%JWT_SECRET%').Replace('change-me-to-a-random-admin-password','%ADMIN_SECRET%'); Set-Content -Encoding Ascii .env $c"
echo [OK] .env created with secure random JWT and admin secrets.
echo [i] The generated admin password is stored in %INSTALL_DIR%\.env.
) else (
echo [OK] .env already exists — keeping existing config.
)
Expand Down
7 changes: 6 additions & 1 deletion Gensui-Docker-Install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,17 @@ if [ ! -f ".env" ]; then

# Generate random JWT secret
JWT_SECRET=$(openssl rand -base64 48 | tr -d '\n' | tr -d '=' | tr -d '+' | tr -d '/')
ADMIN_SECRET=$(openssl rand -base64 36 | tr -d '\n' | tr -d '=' | tr -d '+' | tr -d '/')
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' "s/change-me-to-a-random-64-char-string/$JWT_SECRET/" .env
sed -i '' "s/change-me-to-a-random-admin-password/$ADMIN_SECRET/" .env
else
sed -i "s/change-me-to-a-random-64-char-string/$JWT_SECRET/" .env
sed -i "s/change-me-to-a-random-admin-password/$ADMIN_SECRET/" .env
fi
echo -e " ${GREEN}✅ .env created with secure random JWT secret.${NC}"
echo -e " ${GREEN}✅ .env created with secure random JWT and admin secrets.${NC}"
echo -e " ${GRAY}The generated admin password is stored in $INSTALL_DIR/.env (mode 600).${NC}"
chmod 600 .env
else
echo -e " ${GREEN}✅ .env already exists — keeping existing config.${NC}"
fi
Expand Down
Loading
Loading