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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ on:
pull_request:
branches: [main]

permissions:
contents: read

jobs:
shellcheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@2.0.0
uses: ludeeus/action-shellcheck@94e0aab03ca135d11a35e5bfc14e6746dc56e7e9 # v2.0.0
with:
scandir: '.'
severity: warning
Expand Down
59 changes: 59 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Security Checks

on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 9 * * 1' # Weekly Monday 9am UTC

permissions:
contents: read

jobs:
shellcheck-strict:
name: ShellCheck (error severity)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run ShellCheck at error severity
uses: ludeeus/action-shellcheck@94e0aab03ca135d11a35e5bfc14e6746dc56e7e9 # v2.0.0
with:
scandir: '.'
severity: error
ignore_paths: terminal-academy node_modules

secret-scan:
name: Secret scanning
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Scan for hardcoded secrets
run: |
# Fail if real-looking API keys are found in tracked files
if git ls-files | xargs grep -lniE \
"(sk-ant-api|ghp_[0-9A-Za-z]{36}|xoxb-[0-9A-Za-z-]+|AKIA[0-9A-Z]{16})" \
2>/dev/null | grep -v ".git"; then
echo "::error::Potential hardcoded secrets detected — see matches above"
exit 1
fi
echo "No hardcoded secrets detected"

download-url-check:
name: Verify pinned download URLs are reachable
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check SKILL_URL in step-9
run: |
COMMIT=$(grep 'SKILL_COMMIT=' step-9/step-9-install.sh | head -1 | cut -d'"' -f2)
URL="https://raw.githubusercontent.com/lorecraft-io/cli-maxxing/${COMMIT}/step-9/safetycheck-skill/SKILL.md"
HTTP_STATUS=$(curl -o /dev/null -s -w "%{http_code}" "$URL")
if [ "$HTTP_STATUS" != "200" ]; then
echo "::error::Pinned SKILL_URL returned HTTP $HTTP_STATUS — commit SHA may be invalid"
exit 1
fi
echo "SKILL_URL OK (HTTP 200) for commit $COMMIT"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ CLAUDE.md
*.cert
*.p12
*.pfx
credentials.json

# Node (defensive — step scripts may install deps)
node_modules/
5 changes: 3 additions & 2 deletions README-SECTIONS/cheat-sheet.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,10 @@ These are custom skills installed by the setup scripts. Type them inside a Claud
| `/rmini do the thing` | Step 3 | Launch a compact 5-agent swarm — same power, tighter team |
| `/rhive <goal>` | Step 3 | Launch a queen-led autonomous hive-mind with raft consensus |
| `/w4w` | Step 3 | Maximum attention to detail — word for word, line for line. No skipping, no summarizing. Also works without the slash — just type `w4w` |
| `/safetycheck` | Step 9 | Security audit — scans any project for exposed keys, missing rate limiting, input sanitization gaps, dependency vulnerabilities, and insecure configurations. Also responds to "run a safety check" in plain English |


> These are **explicit triggers** — you type the command to activate the skill. This is different from the auto-triggered tools below, which respond to natural language. Exception: `/w4w` also works without the slash — just type `w4w` anywhere in your message. `/rmini` is the compact version of `/rswarm` — 5 agents instead of 15.
> These are **explicit triggers** — you type the command to activate the skill. This is different from the auto-triggered tools below, which respond to natural language. Exception: `/w4w` also works without the slash — just type `w4w` anywhere in your message. `/rmini` is the compact version of `/rswarm` — 5 agents instead of 15. `/safetycheck` also works in natural language.

---

Expand All @@ -125,7 +126,7 @@ These activate on their own when Claude detects a relevant task via natural lang
| Excalidraw | Add-on | Natural language — diagrams, flowcharts, whiteboard sketches | "Draw a system architecture diagram" |
| Gamma | Add-on | Natural language — presentations, documents, webpages | "Create a pitch deck for my startup" |

> **Key distinction:** Slash commands (`/rswarm`, `/rmini`, `/rhive`, `/w4w`) require you to type the command. Everything in this table works by just talking to Claude naturally.
> **Key distinction:** Slash commands (`/rswarm`, `/rmini`, `/rhive`, `/w4w`, `/safetycheck`) require you to type the command. Everything in this table works by just talking to Claude naturally.
>
> **Add-on tools** are not part of the step-by-step setup — they're optional MCP servers you can connect separately. Claude auto-detects them when they're installed.

Expand Down
3 changes: 2 additions & 1 deletion README-SECTIONS/step-ordering.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Run the steps in this order:
| 6 | Productivity Tools | Motion Calendar + Notion (pick what you use) |
| 7 | Second Brain | Obsidian vault setup + data import (7a-7d) |
| 8 | Telegram | Telegram bot setup — message Claude from your phone |
| 9 | Safety Check | Security auditing — 8 API checks + 12 MCP checks for tool poisoning, DNS rebinding, supply chain attacks |
| **Final** | **Status Line** | **Final config — status indicators, system health check** |

> **Note:** Step 6 (Productivity Tools) is all optional — install only the tools you use. Step 7 (Second Brain) is the biggest step with four sub-parts (7a-7d). Step 8 (Telegram) is interactive — it walks you through creating a bot and pasting your token. The Final Step (Status Line) is the wrap-up that wires everything together — your status indicators show what's active across all the tools.
> **Note:** Step 6 (Productivity Tools) is all optional — install only the tools you use. Step 7 (Second Brain) is the biggest step with four sub-parts (7a-7d). Step 8 (Telegram) is interactive — it walks you through creating a bot and pasting your token. Step 9 (Safety Check) installs the `/safetycheck` security audit skill — 8 core checks for any project, plus 12 MCP-specific checks when an MCP project is detected (20 total). The Final Step (Status Line) is the wrap-up that wires everything together — your status indicators show what's active across all the tools.
2 changes: 1 addition & 1 deletion step-1/step-1-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ install_claude_code() {
echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$SHELL_RC"
success "Added ~/.local/bin to PATH in $SHELL_RC"
else
success "~/.local/bin already in PATH"
success "$HOME/.local/bin already in PATH"
fi

# Install cbrain command (2ndBrain + skip-permissions)
Expand Down
12 changes: 5 additions & 7 deletions step-7/step-7d-wire-vault.sh
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,14 @@ echo " Projects: $PROJECT_COUNT project folders"
echo " Total: $TOTAL_NOTES notes"
echo ""

# Auto-link orphan files to their parent project
# Auto-link orphan files to their parent project (idempotent — skips if link already present)
info "Linking orphan files to parent projects..."
LINK_COUNT=0
while IFS= read -r f; do
if ! grep -q '\[\[' "$f" 2>/dev/null; then
project=$(echo "$f" | sed "s|$VAULT_PATH/07-Projects/||" | cut -d'/' -f1)
if [ -n "$project" ]; then
printf "\n\n---\n[[%s]]\n" "$project" >> "$f"
LINK_COUNT=$((LINK_COUNT + 1))
fi
project=$(echo "$f" | sed "s|$VAULT_PATH/07-Projects/||" | cut -d'/' -f1)
if [ -n "$project" ] && ! grep -qF "[[${project}]]" "$f" 2>/dev/null; then
printf "\n\n---\n[[%s]]\n" "$project" >> "$f"
LINK_COUNT=$((LINK_COUNT + 1))
fi
done < <(find "$VAULT_PATH/07-Projects" -name '*.md')
success "Linked orphan files to parent projects"
Expand Down
24 changes: 12 additions & 12 deletions step-9/safetycheck-skill/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ git ls-files 2>/dev/null | grep -iE "\.env$"

**MCP Config scan** (if MCP detected) — Scan `.mcp.json`, `claude_desktop_config.json`, `.cursor/mcp.json` for hardcoded secrets in `env` blocks:
```bash
grep -r '"env"' .mcp.json claude_desktop_config.json .cursor/mcp.json 2>/dev/null | grep -iE '(sk-[a-zA-Z0-9]{20,}|AKIA[0-9A-Z]{16}|ghp_[a-zA-Z0-9]{36}|AIzaSy[a-zA-Z0-9_-]{30,}|xox[bpsa]-[a-zA-Z0-9-]+)'
grep -rn '"env"' . --include=".mcp.json" --include="claude_desktop_config.json" 2>/dev/null | grep -iE '(sk-[a-zA-Z0-9]{20,}|AKIA[0-9A-Z]{16}|ghp_[a-zA-Z0-9]{36}|AIzaSy[a-zA-Z0-9_-]{30,}|xox[bpsa]-[a-zA-Z0-9-]+)'
```

Check if MCP configs are tracked in git:
Expand Down Expand Up @@ -344,7 +344,7 @@ Verify TLS is enforced and DNS rebinding protection is active.
**Checks:**
```bash
# Check for HTTP (non-HTTPS, non-localhost) in MCP configs
grep -rniE '"url"\s*:\s*"http://' .mcp.json claude_desktop_config.json 2>/dev/null | grep -vE '(localhost|127\.0\.0\.1|::1)'
grep -rniE '"url"\s*:\s*"http://' . --include=".mcp.json" --include="claude_desktop_config.json" 2>/dev/null | grep -vE '(localhost|127\.0\.0\.1|::1)'

# Check for 0.0.0.0 binding without auth
grep -rniE '(0\.0\.0\.0|host:\s*["'"'"']0\.0\.0\.0)' --include="*.ts" --include="*.js" --include="*.py" .
Expand Down Expand Up @@ -395,10 +395,10 @@ Check for over-privileged tokens, missing expiration, and insecure storage.

```bash
# Check for wildcard/broad OAuth scopes in MCP config or auth code
grep -rniE '(mail\.google\.com/|calendar\.google\.com/|drive\.google\.com/|scope.*\*|scope.*"all"|scope.*"full")' --include="*.ts" --include="*.js" --include="*.py" .mcp.json 2>/dev/null
grep -rniE '(mail\.google\.com/|calendar\.google\.com/|drive\.google\.com/|scope.*\*|scope.*"all"|scope.*"full")' --include="*.ts" --include="*.js" --include="*.py" --include=".mcp.json" . 2>/dev/null

# Check for access tokens stored in plaintext
grep -rniE '("access_token"\s*:\s*"[^"]{20,}"|token\s*=\s*["'"'"'][^"'"'"']{20,})' .mcp.json claude_desktop_config.json 2>/dev/null
grep -rniE '("access_token"\s*:\s*"[^"]{20,}"|token\s*=\s*["'"'"'][^"'"'"']{20,})' . --include=".mcp.json" --include="claude_desktop_config.json" 2>/dev/null

# Check for long-lived tokens (no expiry)
grep -rniE '(expires_in.*86400|expires_in.*[0-9]{6,}|no.*expir|never.*expir)' --include="*.ts" --include="*.js" .
Expand Down Expand Up @@ -486,10 +486,10 @@ grep -rn "hostHeaderValidation\|localhostHostValidation\|createMcpExpressApp" --

```bash
# @latest floating versions in MCP config (rug-pull risk)
grep -rniE '"@latest"|npx.*@latest' .mcp.json claude_desktop_config.json .cursor/mcp.json 2>/dev/null
grep -rniE '"@latest"|npx.*@latest' . --include=".mcp.json" --include="claude_desktop_config.json" 2>/dev/null

# npx -y without pinned version (auto-install from potentially poisoned package)
grep -rniE 'npx.*-y' .mcp.json claude_desktop_config.json 2>/dev/null | grep -vE '@[0-9]'
grep -rniE 'npx.*-y' . --include=".mcp.json" --include="claude_desktop_config.json" 2>/dev/null | grep -vE '@[0-9]'

# Lockfile check
ls package-lock.json yarn.lock pnpm-lock.yaml bun.lockb 2>/dev/null || echo "NO_LOCKFILE"
Expand All @@ -498,7 +498,7 @@ ls package-lock.json yarn.lock pnpm-lock.yaml bun.lockb 2>/dev/null || echo "NO_
node -e "const p=require('./package.json'); console.log(p.files ? 'HAS_FILES_WHITELIST' : 'NO_FILES_WHITELIST');" 2>/dev/null

# Shell metacharacters in MCP config args (command injection via config)
grep -rniE '"args"\s*:\s*\[' .mcp.json claude_desktop_config.json 2>/dev/null | grep -E '[;|&\$\`]'
grep -rniE '"args"\s*:\s*\[' . --include=".mcp.json" --include="claude_desktop_config.json" 2>/dev/null | grep -E '[;|&\$\`]'
```

**Severity**: HIGH for `@latest` in MCP config. HIGH for no lockfile. HIGH for shell metacharacters in args arrays. MEDIUM for no files whitelist on published MCP server. PASS if pinned and locked.
Expand All @@ -515,13 +515,13 @@ Verify tool invocations are logged with structured data.

```bash
# Check for structured logging library
grep -rn "winston\|pino\|bunyan\|log4js\|structlog\|logging\.getLogger" package.json requirements.txt 2>/dev/null
grep -rn "winston\|pino\|bunyan\|log4js\|structlog\|logging\.getLogger" . --include="package.json" --include="requirements.txt" 2>/dev/null

# Check for MCP logging notifications
grep -rn "sendLoggingMessage\|LoggingMessageNotification\|setLoggingLevel\|notifications/message" --include="*.ts" --include="*.js" .

# Check for observability integration
grep -rn "opentelemetry\|datadog\|sentry\|splunk\|elastic-apm" package.json 2>/dev/null
grep -rn "opentelemetry\|datadog\|sentry\|splunk\|elastic-apm" . --include="package.json" 2>/dev/null
```

Compare: count tool registrations (`server.tool` / `@mcp.tool`) vs structured logging references. If tools > 0 and structured logging = 0, flag it.
Expand All @@ -538,13 +538,13 @@ Check for floating version references that enable rug-pull attacks.

```bash
# @latest in any MCP config
grep -rniE '"@latest"' .mcp.json claude_desktop_config.json .cursor/mcp.json 2>/dev/null
grep -rniE '"@latest"' . --include=".mcp.json" --include="claude_desktop_config.json" 2>/dev/null

# npx without pinned version in MCP config commands
grep -rniE '"command"\s*:\s*"npx"' .mcp.json claude_desktop_config.json 2>/dev/null
grep -rniE '"command"\s*:\s*"npx"' . --include=".mcp.json" --include="claude_desktop_config.json" 2>/dev/null

# Verify packages have pinned versions (not @latest)
grep -rniE '@[a-z0-9-]+/[a-z0-9-]+' .mcp.json claude_desktop_config.json 2>/dev/null | grep -v '@[0-9]' | grep -v '@latest'
grep -rniE '@[a-z0-9-]+/[a-z0-9-]+' . --include=".mcp.json" --include="claude_desktop_config.json" 2>/dev/null | grep -v '@[0-9]' | grep -v '@latest'

# Check if any MCP server hashes tool definitions (integrity verification)
grep -rn "createHash\|sha256\|sha-256\|integrity\|checksum" --include="*.ts" --include="*.js" . | grep -iE "(tool|description|schema)"
Expand Down
23 changes: 22 additions & 1 deletion step-9/step-9-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,11 @@ verify_prerequisites() {
install_skill() {
SKILL_DIR="$HOME/.claude/skills/safetycheck"
SKILL_FILE="$SKILL_DIR/SKILL.md"
SKILL_URL="https://raw.githubusercontent.com/lorecraft-io/cli-maxxing/main/step-9/safetycheck-skill/SKILL.md"
# Pinned to a specific commit SHA — prevents rug-pull via mutable branch ref
# To update: change the SHA to the new commit and update SKILL_SHA256 to match
SKILL_COMMIT="7b449b652d946a8eef9aca65f0c8e182b4fb80f7"
SKILL_URL="https://raw.githubusercontent.com/lorecraft-io/cli-maxxing/${SKILL_COMMIT}/step-9/safetycheck-skill/SKILL.md"
SKILL_SHA256="77e1ef1127fa35cd860925a652b96dd062ab080d438787b3bde348176597ab12"

info "Creating skill directory..."
mkdir -p "$SKILL_DIR"
Expand Down Expand Up @@ -142,6 +146,23 @@ install_skill() {
return
fi

# Verify SHA-256 integrity — protects against corrupted download or tampered content
if command -v shasum &>/dev/null; then
ACTUAL_SHA=$(shasum -a 256 "$SKILL_FILE" | cut -d' ' -f1)
if [ "$ACTUAL_SHA" = "$SKILL_SHA256" ]; then
success "Skill file integrity verified (sha256 match)"
else
soft_fail "Skill file sha256 mismatch — file may be corrupt or tampered. Expected: ${SKILL_SHA256:0:16}..."
fi
elif command -v sha256sum &>/dev/null; then
ACTUAL_SHA=$(sha256sum "$SKILL_FILE" | cut -d' ' -f1)
if [ "$ACTUAL_SHA" = "$SKILL_SHA256" ]; then
success "Skill file integrity verified (sha256 match)"
else
soft_fail "Skill file sha256 mismatch — file may be corrupt or tampered. Expected: ${SKILL_SHA256:0:16}..."
fi
fi

# Verify the file contains expected content
if grep -q "safetycheck" "$SKILL_FILE" 2>/dev/null; then
success "Skill file content verified"
Expand Down
5 changes: 2 additions & 3 deletions step-final/step-final-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -202,14 +202,14 @@ fi
# =============================================================================
info "Checking for project-level statusLine overrides..."
FOUND_OVERRIDES=0
for PROJECT_SETTINGS in $(find "$HOME/Desktop" "$HOME/Documents" -maxdepth 5 -path "*/.claude/settings.json" -not -path "$HOME/.claude/settings.json" 2>/dev/null); do
while IFS= read -r PROJECT_SETTINGS; do
if command -v jq &>/dev/null && jq -e '.statusLine' "$PROJECT_SETTINGS" &>/dev/null 2>&1; then
jq 'del(.statusLine)' "$PROJECT_SETTINGS" > "${PROJECT_SETTINGS}.tmp" \
&& mv "${PROJECT_SETTINGS}.tmp" "$PROJECT_SETTINGS"
warn "Removed statusLine override from: $PROJECT_SETTINGS"
FOUND_OVERRIDES=$((FOUND_OVERRIDES + 1))
fi
done
done < <(find "$HOME/Desktop" "$HOME/Documents" -maxdepth 5 -path "*/.claude/settings.json" -not -path "$HOME/.claude/settings.json" 2>/dev/null)
if [ "$FOUND_OVERRIDES" -eq 0 ]; then
success "No project-level statusLine overrides found"
else
Expand All @@ -234,7 +234,6 @@ esac

HC_PASS=0
HC_FAIL=0
HC_ISSUES=""

# --- Shell aliases ---
for alias_check in \
Expand Down
4 changes: 2 additions & 2 deletions uninstall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -387,9 +387,9 @@ fi
if grep -q '\.local/bin' "$SHELL_RC" 2>/dev/null; then
sed -i.bak '/\.local\/bin/d' "$SHELL_RC" 2>/dev/null || true
rm -f "${SHELL_RC}.bak"
success "~/.local/bin PATH entry removed from $SHELL_RC"
success "$HOME/.local/bin PATH entry removed from $SHELL_RC"
else
skip "~/.local/bin PATH entry (not found in $SHELL_RC)"
skip "$HOME/.local/bin PATH entry (not found in $SHELL_RC)"
fi

# cbrain and cbraintg commands
Expand Down
1 change: 0 additions & 1 deletion update.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ set -uo pipefail
# Usage: curl -fsSL <hosted-url>/update.sh | bash
# =============================================================================

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
Expand Down
Loading