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
21 changes: 21 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Code ownership for security-sensitive paths
# These files require maintainer review on every PR

# Security-critical: auth, secrets, OAuth flow
/src/auth-server.ts @patlux
/src/oauth.ts @patlux
/src/converters.ts @patlux

# CI/CD pipeline
.github/workflows/ @patlux

# Dependencies
package.json @patlux
package-lock.json @patlux

# Security tooling
.semgrep/ @patlux
.gitleaks.toml @patlux

# This file itself
.github/CODEOWNERS @patlux
140 changes: 140 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ on:
workflow_dispatch:

jobs:
# ──────────────────────────────────────────────────────────
# Code correctness
# ──────────────────────────────────────────────────────────
typecheck:
runs-on: ubuntu-latest
steps:
Expand All @@ -29,3 +32,140 @@ jobs:
cache: npm
- run: npm ci
- run: npm run format:check

# ──────────────────────────────────────────────────────────
# Code-level vulnerability scanning (SAST)
# ──────────────────────────────────────────────────────────
codeql:
name: CodeQL SAST
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- uses: actions/checkout@v4
- uses: github/codeql-action/init@v3
with:
languages: javascript-typescript
queries: +security-extended,security-and-quality
- uses: github/codeql-action/autobuild@v3
- uses: github/codeql-action/analyze@v3

# ──────────────────────────────────────────────────────────
# Custom static analysis — pi extension attack patterns
# ──────────────────────────────────────────────────────────
semgrep:
name: Semgrep — pi extension audit
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
container:
image: semgrep/semgrep:latest
steps:
- uses: actions/checkout@v4
- name: Run Semgrep with custom rules
run: |
semgrep --config .semgrep/ --error --output semgrep-report.sarif --sarif .
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: semgrep-report.sarif
category: semgrep-pi-audit
if: always()

# ──────────────────────────────────────────────────────────
# Hardcoded secrets detection
# ──────────────────────────────────────────────────────────
gitleaks:
name: Gitleaks — secrets scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITLEAKS_ENABLE_COMMENTS: "true"

# ──────────────────────────────────────────────────────────
# Dependency supply-chain security
# ──────────────────────────────────────────────────────────
deps-review:
name: Dependency review
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Check if dependency graph is enabled
run: |
echo "Dependency review requires enabling Dependency graph in repo settings."
echo "Go to: https://github.com/patlux/pi-commandcode-provider/settings/security_analysis"
echo "Enable: Dependency graph"
- uses: actions/dependency-review-action@v4
with:
fail-on-severity: high
allow-licenses: MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, 0BSD
comment-summary-in-pr: always
continue-on-error: true

deps-audit:
name: npm audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm audit --audit-level=moderate
- name: Exit gracefully on audit findings
if: failure()
run: |
echo "::warning::npm audit found vulnerabilities. Review and patch before merging."

# ──────────────────────────────────────────────────────────
# Postinstall script check — prevents install-time malware
# ──────────────────────────────────────────────────────────
check-scripts:
name: Check lifecycle scripts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check for malicious lifecycle scripts
run: |
echo "::group::package.json scripts"
node -e "
const pkg = require('./package.json');
const dangerous = ['preinstall','install','postinstall','prepublish','prepare'];
const found = dangerous.filter(s => pkg.scripts && pkg.scripts[s]);
if (found.length) {
found.forEach(s => console.log('WARNING: package.json has "' + s + '":', pkg.scripts[s]));
process.exit(1);
} else {
console.log('No dangerous lifecycle scripts in package.json');
}
"
echo "::endgroup::"
echo "::group::dependency scripts (top-level)"
npm query '.scripts' --all 2>/dev/null | node -e "
const d = require('fs').readFileSync('/dev/stdin','utf8');
if (!d.trim()) { console.log('No dependency scripts found'); process.exit(0); }
let pkgs;
try { pkgs = JSON.parse(d); } catch(e) { console.log('Could not parse npm query output'); process.exit(0); }
if (!Array.isArray(pkgs)) pkgs = Object.values(pkgs);
const withScripts = pkgs.filter(p => p && p.pkgid && p.scripts);
withScripts.forEach(p => {
const dangerous = ['preinstall','install','postinstall','prepublish','prepare'];
const has = Object.keys(p.scripts || {}).filter(s => dangerous.includes(s));
if (has.length) console.log('⚠', p.pkgid, 'has scripts:', Object.keys(p.scripts));
});
if (withScripts.length === 0) console.log('No dependency lifecycle scripts');
" 2>&1 || true
echo "::endgroup::"
51 changes: 51 additions & 0 deletions .gitleaks.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Custom Gitleaks config for pi-commandcode-provider.
# Extends the default rule set with patterns specific to pi extensions.

title = "pi-commandcode-provider secret scan"

[allowlist]
description = "Known safe paths and test fixtures"
paths = [
# Test helpers with mock credentials
"tests/test-oauth.ts",
"tests/test-stream.ts",
"tests/test-pure-functions.ts",
"tests/test-pi-local.mjs",
"tests/test-omp-compat.mjs",
# CI workflows reference NPM_TOKEN secret name
".github/workflows/publish.yml",
]

# ──────────────────────────────────────────────────────────
# Project-specific rules
# ──────────────────────────────────────────────────────────

[[rules]]
id = "pi-commandcode-api-key"
description = "Command Code API key hardcoded in source"
regex = '''(?i)COMMANDCODE_API_KEY\s*[=:]\s*['"](user_[A-Za-z0-9_-]{10,}|cc_[A-Za-z0-9_-]{10,})['"]'''
tags = ["pi-extension", "commandcode", "api-key"]

[[rules]]
id = "pi-auth-file-pattern"
description = "In-line pi auth.json content in source code"
regex = '''['"](apiKey|commandcode|command-code)['"]\s*:\s*['"]user_[A-Za-z0-9_-]{10,}['"]'''
tags = ["pi-extension", "auth"]

[[rules]]
id = "pi-hardcoded-bearer-token"
description = "Hardcoded Bearer token (20+ chars) in Authorization header"
regex = '''Bearer [A-Za-z0-9_.\-]{20,}'''
tags = ["pi-extension", "auth-token"]

[[rules]]
id = "pi-oauth-callback-url"
description = "OAuth callback URL with hardcoded key"
regex = '''callbackUrl\s*=\s*['"]http://localhost:\d+/callback['"]'''
tags = ["pi-extension", "oauth"]

[[rules]]
id = "pi-test-api-key"
description = "Test API key value that looks real"
regex = '''(user_testKey|mock-key|fake-key|test-api-key)'''
tags = ["pi-extension", "test"]
Loading
Loading