castops 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- castops-0.2.1/.github/workflows/deploy-website.yml +44 -0
- castops-0.2.1/.github/workflows/devsecops.yml +148 -0
- castops-0.2.1/.github/workflows/publish.yml +68 -0
- castops-0.2.1/.gitignore +8 -0
- castops-0.2.1/CHANGELOG.md +87 -0
- castops-0.2.1/CONTRIBUTING.md +279 -0
- castops-0.2.1/LICENSE +201 -0
- castops-0.2.1/PKG-INFO +381 -0
- castops-0.2.1/README.md +365 -0
- castops-0.2.1/README.zh.md +332 -0
- castops-0.2.1/SECURITY.md +81 -0
- castops-0.2.1/TODOS.md +116 -0
- castops-0.2.1/dashboard/generate.py +319 -0
- castops-0.2.1/dashboard/template.html +290 -0
- castops-0.2.1/docs/cli-reference.md +175 -0
- castops-0.2.1/docs/dashboard-guide.md +144 -0
- castops-0.2.1/docs/getting-started.md +159 -0
- castops-0.2.1/docs/gitlab-guide.md +136 -0
- castops-0.2.1/docs/pipeline-reference.md +500 -0
- castops-0.2.1/docs/plugin-guide.md +269 -0
- castops-0.2.1/docs/policy-reference.md +137 -0
- castops-0.2.1/docs/zh/cli-reference.md +171 -0
- castops-0.2.1/docs/zh/dashboard-guide.md +157 -0
- castops-0.2.1/docs/zh/getting-started.md +207 -0
- castops-0.2.1/docs/zh/gitlab-guide.md +166 -0
- castops-0.2.1/docs/zh/pipeline-reference.md +427 -0
- castops-0.2.1/docs/zh/policy-reference.md +201 -0
- castops-0.2.1/policy/default.rego +21 -0
- castops-0.2.1/policy/permissive.rego +22 -0
- castops-0.2.1/policy/strict.rego +34 -0
- castops-0.2.1/pyproject.toml +39 -0
- castops-0.2.1/setup.cfg +4 -0
- castops-0.2.1/src/cast_cli/__init__.py +1 -0
- castops-0.2.1/src/cast_cli/detect.py +31 -0
- castops-0.2.1/src/cast_cli/install.py +39 -0
- castops-0.2.1/src/cast_cli/main.py +145 -0
- castops-0.2.1/src/cast_cli/templates/__init__.py +0 -0
- castops-0.2.1/src/cast_cli/templates/gitlab/__init__.py +0 -0
- castops-0.2.1/src/cast_cli/templates/gitlab/go/__init__.py +0 -0
- castops-0.2.1/src/cast_cli/templates/gitlab/go/devsecops.yml +139 -0
- castops-0.2.1/src/cast_cli/templates/gitlab/nodejs/__init__.py +0 -0
- castops-0.2.1/src/cast_cli/templates/gitlab/nodejs/devsecops.yml +139 -0
- castops-0.2.1/src/cast_cli/templates/gitlab/python/__init__.py +0 -0
- castops-0.2.1/src/cast_cli/templates/gitlab/python/devsecops.yml +139 -0
- castops-0.2.1/src/cast_cli/templates/go/__init__.py +0 -0
- castops-0.2.1/src/cast_cli/templates/go/devsecops.yml +202 -0
- castops-0.2.1/src/cast_cli/templates/nodejs/__init__.py +0 -0
- castops-0.2.1/src/cast_cli/templates/nodejs/devsecops.yml +202 -0
- castops-0.2.1/src/cast_cli/templates/python/__init__.py +0 -0
- castops-0.2.1/src/cast_cli/templates/python/devsecops.yml +196 -0
- castops-0.2.1/src/castops.egg-info/PKG-INFO +381 -0
- castops-0.2.1/src/castops.egg-info/SOURCES.txt +83 -0
- castops-0.2.1/src/castops.egg-info/dependency_links.txt +1 -0
- castops-0.2.1/src/castops.egg-info/entry_points.txt +2 -0
- castops-0.2.1/src/castops.egg-info/requires.txt +5 -0
- castops-0.2.1/src/castops.egg-info/top_level.txt +1 -0
- castops-0.2.1/templates/github/publish-dashboard.yml +83 -0
- castops-0.2.1/templates/gitlab/go/devsecops.yml +139 -0
- castops-0.2.1/templates/gitlab/nodejs/devsecops.yml +139 -0
- castops-0.2.1/templates/gitlab/python/devsecops.yml +139 -0
- castops-0.2.1/templates/go/devsecops.yml +202 -0
- castops-0.2.1/templates/nodejs/devsecops.yml +202 -0
- castops-0.2.1/templates/python/devsecops.yml +196 -0
- castops-0.2.1/tests/__init__.py +0 -0
- castops-0.2.1/tests/test_cli.py +71 -0
- castops-0.2.1/tests/test_dashboard.py +158 -0
- castops-0.2.1/tests/test_detect.py +71 -0
- castops-0.2.1/tests/test_install.py +113 -0
- castops-0.2.1/website/_config.yml +1 -0
- castops-0.2.1/website/docs/cli-reference.html +507 -0
- castops-0.2.1/website/docs/dashboard-guide.html +415 -0
- castops-0.2.1/website/docs/getting-started.html +446 -0
- castops-0.2.1/website/docs/gitlab-guide.html +421 -0
- castops-0.2.1/website/docs/pipeline-reference.html +811 -0
- castops-0.2.1/website/docs/plugin-guide.html +539 -0
- castops-0.2.1/website/docs/policy-reference.html +446 -0
- castops-0.2.1/website/index.html +779 -0
- castops-0.2.1/website/zh/docs/cli-reference.html +507 -0
- castops-0.2.1/website/zh/docs/dashboard-guide.html +415 -0
- castops-0.2.1/website/zh/docs/getting-started.html +446 -0
- castops-0.2.1/website/zh/docs/gitlab-guide.html +421 -0
- castops-0.2.1/website/zh/docs/pipeline-reference.html +811 -0
- castops-0.2.1/website/zh/docs/plugin-guide.html +539 -0
- castops-0.2.1/website/zh/docs/policy-reference.html +446 -0
- castops-0.2.1/website/zh/index.html +618 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# CAST — Deploy official website to GitHub Pages
|
|
2
|
+
#
|
|
3
|
+
# Triggers on push to main (website/ changes).
|
|
4
|
+
# Serves website/ directory at https://castops.github.io/cast/
|
|
5
|
+
|
|
6
|
+
name: Deploy Website
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
branches: [main]
|
|
11
|
+
paths:
|
|
12
|
+
- 'website/**'
|
|
13
|
+
workflow_dispatch:
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
pages: write
|
|
18
|
+
id-token: write
|
|
19
|
+
|
|
20
|
+
concurrency:
|
|
21
|
+
group: pages
|
|
22
|
+
cancel-in-progress: false
|
|
23
|
+
|
|
24
|
+
jobs:
|
|
25
|
+
deploy:
|
|
26
|
+
name: Deploy to GitHub Pages
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
environment:
|
|
29
|
+
name: github-pages
|
|
30
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v4
|
|
33
|
+
|
|
34
|
+
- name: Setup Pages
|
|
35
|
+
uses: actions/configure-pages@v5
|
|
36
|
+
|
|
37
|
+
- name: Upload artifact
|
|
38
|
+
uses: actions/upload-pages-artifact@v3
|
|
39
|
+
with:
|
|
40
|
+
path: website/
|
|
41
|
+
|
|
42
|
+
- name: Deploy to GitHub Pages
|
|
43
|
+
id: deployment
|
|
44
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# CAST — DevSecOps Pipeline for Python
|
|
2
|
+
# https://github.com/castops/cast
|
|
3
|
+
#
|
|
4
|
+
# Includes:
|
|
5
|
+
# 1. Secrets Detection — Gitleaks
|
|
6
|
+
# 2. SAST — Semgrep
|
|
7
|
+
# 3. SCA — pip-audit
|
|
8
|
+
# 4. Container Security — Trivy (skipped if no Dockerfile)
|
|
9
|
+
# 5. Code Quality — Ruff
|
|
10
|
+
# 6. Security Gate — blocks merge on critical findings
|
|
11
|
+
|
|
12
|
+
name: CAST DevSecOps
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
push:
|
|
16
|
+
branches: [main, master]
|
|
17
|
+
pull_request:
|
|
18
|
+
branches: [main, master]
|
|
19
|
+
workflow_dispatch:
|
|
20
|
+
|
|
21
|
+
permissions:
|
|
22
|
+
contents: read
|
|
23
|
+
security-events: write # required for SARIF upload to GitHub Security tab
|
|
24
|
+
actions: read
|
|
25
|
+
|
|
26
|
+
jobs:
|
|
27
|
+
|
|
28
|
+
# ── 1. Secrets Detection ───────────────────────────────────────────────────
|
|
29
|
+
secrets:
|
|
30
|
+
name: Secrets Detection
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
with:
|
|
35
|
+
fetch-depth: 0 # full history so Gitleaks can scan all commits
|
|
36
|
+
- name: Install Gitleaks
|
|
37
|
+
run: |
|
|
38
|
+
curl -sSfL https://github.com/gitleaks/gitleaks/releases/download/v8.27.2/gitleaks_8.27.2_linux_x64.tar.gz \
|
|
39
|
+
-o /tmp/gitleaks.tar.gz
|
|
40
|
+
tar -xzf /tmp/gitleaks.tar.gz -C /tmp gitleaks
|
|
41
|
+
sudo mv /tmp/gitleaks /usr/local/bin/gitleaks
|
|
42
|
+
gitleaks version
|
|
43
|
+
- name: Gitleaks scan
|
|
44
|
+
run: gitleaks detect --source . --verbose --redact
|
|
45
|
+
|
|
46
|
+
# ── 2. SAST ────────────────────────────────────────────────────────────────
|
|
47
|
+
sast:
|
|
48
|
+
name: SAST
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
container:
|
|
51
|
+
image: semgrep/semgrep
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@v4
|
|
54
|
+
- name: Semgrep scan
|
|
55
|
+
# SEMGREP_APP_TOKEN is optional — falls back to open-source rules
|
|
56
|
+
run: semgrep ci --sarif --output=semgrep.sarif || true
|
|
57
|
+
env:
|
|
58
|
+
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
|
|
59
|
+
- name: Ensure SARIF exists
|
|
60
|
+
if: always()
|
|
61
|
+
run: |
|
|
62
|
+
[ -f semgrep.sarif ] || echo '{"version":"2.1.0","$schema":"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json","runs":[]}' > semgrep.sarif
|
|
63
|
+
- name: Upload to GitHub Security tab
|
|
64
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
65
|
+
if: always()
|
|
66
|
+
continue-on-error: true # requires GitHub Advanced Security; skip gracefully if not enabled
|
|
67
|
+
with:
|
|
68
|
+
sarif_file: semgrep.sarif
|
|
69
|
+
|
|
70
|
+
# ── 3. SCA — Dependency Audit ──────────────────────────────────────────────
|
|
71
|
+
sca:
|
|
72
|
+
name: SCA
|
|
73
|
+
runs-on: ubuntu-latest
|
|
74
|
+
steps:
|
|
75
|
+
- uses: actions/checkout@v4
|
|
76
|
+
- uses: actions/setup-python@v5
|
|
77
|
+
with:
|
|
78
|
+
python-version: "3.x"
|
|
79
|
+
cache: pip
|
|
80
|
+
- name: pip-audit
|
|
81
|
+
run: |
|
|
82
|
+
pip install pip-audit
|
|
83
|
+
pip-audit
|
|
84
|
+
|
|
85
|
+
# ── 4. Container Security ──────────────────────────────────────────────────
|
|
86
|
+
container:
|
|
87
|
+
name: Container Security
|
|
88
|
+
runs-on: ubuntu-latest
|
|
89
|
+
steps:
|
|
90
|
+
- uses: actions/checkout@v4
|
|
91
|
+
- name: Check for Dockerfile
|
|
92
|
+
id: check_dockerfile
|
|
93
|
+
run: |
|
|
94
|
+
if [ -f Dockerfile ]; then
|
|
95
|
+
echo "found=true" >> $GITHUB_OUTPUT
|
|
96
|
+
else
|
|
97
|
+
echo "No Dockerfile found — skipping container scan"
|
|
98
|
+
echo "found=false" >> $GITHUB_OUTPUT
|
|
99
|
+
fi
|
|
100
|
+
- name: Build image
|
|
101
|
+
if: steps.check_dockerfile.outputs.found == 'true'
|
|
102
|
+
run: docker build -t cast-scan:${{ github.sha }} .
|
|
103
|
+
- name: Trivy scan
|
|
104
|
+
if: steps.check_dockerfile.outputs.found == 'true'
|
|
105
|
+
uses: aquasecurity/trivy-action@master
|
|
106
|
+
with:
|
|
107
|
+
image-ref: cast-scan:${{ github.sha }}
|
|
108
|
+
format: sarif
|
|
109
|
+
output: trivy.sarif
|
|
110
|
+
severity: CRITICAL,HIGH
|
|
111
|
+
exit-code: "0" # gate handles blocking, not trivy directly
|
|
112
|
+
- name: Upload to GitHub Security tab
|
|
113
|
+
if: steps.check_dockerfile.outputs.found == 'true'
|
|
114
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
115
|
+
continue-on-error: true # requires GitHub Advanced Security; skip gracefully if not enabled
|
|
116
|
+
with:
|
|
117
|
+
sarif_file: trivy.sarif
|
|
118
|
+
|
|
119
|
+
# ── 5. Code Quality ────────────────────────────────────────────────────────
|
|
120
|
+
quality:
|
|
121
|
+
name: Code Quality
|
|
122
|
+
runs-on: ubuntu-latest
|
|
123
|
+
steps:
|
|
124
|
+
- uses: actions/checkout@v4
|
|
125
|
+
- uses: astral-sh/ruff-action@v1
|
|
126
|
+
|
|
127
|
+
# ── 6. Security Gate ───────────────────────────────────────────────────────
|
|
128
|
+
gate:
|
|
129
|
+
name: Security Gate
|
|
130
|
+
runs-on: ubuntu-latest
|
|
131
|
+
needs: [secrets, sast, sca, quality]
|
|
132
|
+
if: always()
|
|
133
|
+
steps:
|
|
134
|
+
- name: Evaluate results
|
|
135
|
+
run: |
|
|
136
|
+
echo "secrets : ${{ needs.secrets.result }}"
|
|
137
|
+
echo "sast : ${{ needs.sast.result }}"
|
|
138
|
+
echo "sca : ${{ needs.sca.result }}"
|
|
139
|
+
echo "quality : ${{ needs.quality.result }}"
|
|
140
|
+
|
|
141
|
+
if [[ "${{ needs.secrets.result }}" == "failure" ||
|
|
142
|
+
"${{ needs.sast.result }}" == "failure" ||
|
|
143
|
+
"${{ needs.sca.result }}" == "failure" ]]; then
|
|
144
|
+
echo "❌ Security gate failed — merge blocked"
|
|
145
|
+
exit 1
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
echo "✅ All checks passed — safe to merge"
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# CAST — Publish castops to PyPI
|
|
2
|
+
#
|
|
3
|
+
# Triggers on push of a version tag (e.g. v0.2.0).
|
|
4
|
+
# Uses PyPI Trusted Publishing (OIDC) — no API token needed.
|
|
5
|
+
#
|
|
6
|
+
# One-time setup required on PyPI before first run:
|
|
7
|
+
# pypi.org → Manage account → Publishing → Add a new publisher
|
|
8
|
+
# Publisher: GitHub | Owner: castops | Repo: cast
|
|
9
|
+
# Workflow: publish.yml | Environment: pypi
|
|
10
|
+
|
|
11
|
+
name: Publish to PyPI
|
|
12
|
+
|
|
13
|
+
on:
|
|
14
|
+
push:
|
|
15
|
+
tags:
|
|
16
|
+
- 'v*'
|
|
17
|
+
workflow_dispatch:
|
|
18
|
+
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
build:
|
|
24
|
+
name: Build distribution
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v4
|
|
28
|
+
with:
|
|
29
|
+
fetch-depth: 0 # setuptools-scm needs full history for version
|
|
30
|
+
|
|
31
|
+
- uses: actions/setup-python@v5
|
|
32
|
+
with:
|
|
33
|
+
python-version: '3.11'
|
|
34
|
+
|
|
35
|
+
- name: Install build
|
|
36
|
+
run: pip install build
|
|
37
|
+
|
|
38
|
+
- name: Build sdist and wheel
|
|
39
|
+
run: |
|
|
40
|
+
# Strip leading 'v' from tag (v0.2.1 → 0.2.1) so PyPI gets a clean version.
|
|
41
|
+
# SETUPTOOLS_SCM_PRETEND_VERSION overrides the git-derived version entirely.
|
|
42
|
+
VERSION="${GITHUB_REF_NAME#v}"
|
|
43
|
+
SETUPTOOLS_SCM_PRETEND_VERSION="$VERSION" python -m build
|
|
44
|
+
|
|
45
|
+
- name: Upload build artifacts
|
|
46
|
+
uses: actions/upload-artifact@v4
|
|
47
|
+
with:
|
|
48
|
+
name: dist
|
|
49
|
+
path: dist/
|
|
50
|
+
|
|
51
|
+
publish:
|
|
52
|
+
name: Publish to PyPI
|
|
53
|
+
needs: build
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
environment:
|
|
56
|
+
name: pypi
|
|
57
|
+
url: https://pypi.org/project/castops/
|
|
58
|
+
permissions:
|
|
59
|
+
id-token: write # required for OIDC trusted publishing
|
|
60
|
+
steps:
|
|
61
|
+
- name: Download build artifacts
|
|
62
|
+
uses: actions/download-artifact@v4
|
|
63
|
+
with:
|
|
64
|
+
name: dist
|
|
65
|
+
path: dist/
|
|
66
|
+
|
|
67
|
+
- name: Publish to PyPI
|
|
68
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
castops-0.2.1/.gitignore
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to CAST are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
|
+
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## [Unreleased]
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Node.js and Go GitHub Actions templates** — production-ready pipelines for both stacks:
|
|
15
|
+
- Node.js: Gitleaks + Semgrep + npm audit + Trivy + ESLint + conftest gate
|
|
16
|
+
- Go: Gitleaks + Semgrep + govulncheck + Trivy + staticcheck + conftest gate
|
|
17
|
+
- **GitLab CI templates** — full security parity with GitHub Actions for Python, Node.js, and Go:
|
|
18
|
+
- Use `cast init --platform gitlab` to generate a `.gitlab-ci.yml`
|
|
19
|
+
- Findings appear in GitLab's Security dashboard via SARIF artifact reports
|
|
20
|
+
- **Policy as Code (OPA/conftest)** — the security gate now evaluates SARIF findings using
|
|
21
|
+
Rego policies instead of hardcoded shell logic:
|
|
22
|
+
- `policy/default.rego` — blocks on CRITICAL findings (default)
|
|
23
|
+
- `policy/strict.rego` — blocks on HIGH + CRITICAL
|
|
24
|
+
- `policy/permissive.rego` — audit only, never blocks merges
|
|
25
|
+
- Custom policies can be placed in a `policy/` directory alongside the workflow
|
|
26
|
+
- **Static HTML security dashboard** — generate a compliance overview from SARIF results:
|
|
27
|
+
- `dashboard/generate.py`: parses SARIF files and renders a zero-dependency HTML page
|
|
28
|
+
- Red/green status per scan, collapsible finding details, commit SHA and timestamp
|
|
29
|
+
- Deploy to GitHub Pages with `templates/github/publish-dashboard.yml`
|
|
30
|
+
- **`cast init --platform gitlab`** — the CLI now supports GitLab CI as a target platform
|
|
31
|
+
- **Auto-detection of CI platform** — `cast init` detects `.gitlab-ci.yml` and `.github/`
|
|
32
|
+
to determine the platform automatically
|
|
33
|
+
- **Chinese documentation** — full Chinese translation under `docs/zh/` and `README.zh.md`
|
|
34
|
+
- **Test suite** — 65 tests covering project detection, template installation, dashboard
|
|
35
|
+
generation, and CLI behavior
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- **Gitleaks in org repositories** — replaced the GitHub Action (requires paid org license)
|
|
40
|
+
with direct CLI installation from GitHub releases; works on all repositories for free
|
|
41
|
+
- **SARIF upload without GitHub Advanced Security** — upload steps now use `continue-on-error`
|
|
42
|
+
so the pipeline does not fail when GHAS is not enabled on the repository
|
|
43
|
+
- **Semgrep SARIF fallback** — added an "Ensure SARIF exists" step so the upload action
|
|
44
|
+
never fails when Semgrep exits non-zero without creating a SARIF file
|
|
45
|
+
- **Auto-detection priority** — Go and Node.js are now detected before Python, so repositories
|
|
46
|
+
that use `pyproject.toml` only for tooling (Ruff, pre-commit) are not misidentified
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## [0.1.0] — 2024-01-01
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
- **`cast` CLI** — new `castops` Python package installable via `pip install castops`
|
|
55
|
+
- `cast init` command: writes a DevSecOps workflow to `.github/workflows/devsecops.yml`
|
|
56
|
+
- `cast version` command: displays the installed package version
|
|
57
|
+
- Auto-detection of project type from marker files (`pyproject.toml`, `requirements.txt`, etc.)
|
|
58
|
+
- `--force` flag to overwrite existing workflow files
|
|
59
|
+
- `--type` flag to override auto-detection
|
|
60
|
+
- Rich terminal output with color-coded status messages
|
|
61
|
+
- **Python DevSecOps template** — production-ready GitHub Actions workflow including:
|
|
62
|
+
- Secrets Detection via [Gitleaks](https://github.com/gitleaks/gitleaks) (full git history scan)
|
|
63
|
+
- SAST via [Semgrep](https://semgrep.dev) with SARIF upload to GitHub Security tab
|
|
64
|
+
- SCA via [pip-audit](https://pypi.org/project/pip-audit/) for vulnerable dependency detection
|
|
65
|
+
- Container Security via [Trivy](https://trivy.dev) (conditional on Dockerfile presence)
|
|
66
|
+
- Code Quality via [Ruff](https://docs.astral.sh/ruff/)
|
|
67
|
+
- Security Gate job that blocks merges on critical security failures
|
|
68
|
+
- **Manual installation** support via `curl` for teams that prefer not to install the CLI
|
|
69
|
+
|
|
70
|
+
### Technical Details
|
|
71
|
+
|
|
72
|
+
- Python 3.9+ support
|
|
73
|
+
- Templates embedded in the package via `importlib.resources` for offline use
|
|
74
|
+
- MIT-compatible dependencies: [Typer](https://typer.tiangolo.com/) and [Rich](https://rich.readthedocs.io/)
|
|
75
|
+
- Build system: `setuptools` + `setuptools-scm` (version from git tags)
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## [0.0.1] — Initial Commit
|
|
80
|
+
|
|
81
|
+
- Project scaffolding and initial Python workflow template
|
|
82
|
+
- Apache 2.0 license
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
[Unreleased]: https://github.com/castops/cast/compare/v0.1.0...HEAD
|
|
87
|
+
[0.1.0]: https://github.com/castops/cast/releases/tag/v0.1.0
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# Contributing to CAST
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to CAST. This document outlines the process
|
|
4
|
+
for contributing code, templates, and documentation to the project.
|
|
5
|
+
|
|
6
|
+
## Table of Contents
|
|
7
|
+
|
|
8
|
+
- [Code of Conduct](#code-of-conduct)
|
|
9
|
+
- [Getting Started](#getting-started)
|
|
10
|
+
- [Development Setup](#development-setup)
|
|
11
|
+
- [Project Structure](#project-structure)
|
|
12
|
+
- [Adding a New Template](#adding-a-new-template)
|
|
13
|
+
- [Running Tests](#running-tests)
|
|
14
|
+
- [Submitting a Pull Request](#submitting-a-pull-request)
|
|
15
|
+
- [Release Process](#release-process)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Code of Conduct
|
|
20
|
+
|
|
21
|
+
This project follows a standard open-source code of conduct. Be respectful, constructive,
|
|
22
|
+
and collaborative. Harassment of any kind will not be tolerated.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Getting Started
|
|
27
|
+
|
|
28
|
+
Before contributing, please:
|
|
29
|
+
|
|
30
|
+
1. **Search existing issues** to avoid duplicates
|
|
31
|
+
2. **Open an issue** for non-trivial changes to discuss the approach before writing code
|
|
32
|
+
3. **Fork the repository** and create a feature branch from `main`
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Development Setup
|
|
37
|
+
|
|
38
|
+
### Prerequisites
|
|
39
|
+
|
|
40
|
+
- Python 3.9 or higher
|
|
41
|
+
- `git`
|
|
42
|
+
|
|
43
|
+
### Install in Editable Mode
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# Clone your fork
|
|
47
|
+
git clone https://github.com/<your-username>/cast.git
|
|
48
|
+
cd cast
|
|
49
|
+
|
|
50
|
+
# Create and activate a virtual environment
|
|
51
|
+
python -m venv .venv
|
|
52
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
53
|
+
|
|
54
|
+
# Install in editable mode with dev dependencies
|
|
55
|
+
pip install -e ".[dev]"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
> If there is no `[dev]` extras group yet, install the base package with:
|
|
59
|
+
> ```bash
|
|
60
|
+
> pip install -e .
|
|
61
|
+
> ```
|
|
62
|
+
|
|
63
|
+
### Verify Installation
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
cast version
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Project Structure
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
cast/
|
|
75
|
+
├── src/
|
|
76
|
+
│ └── cast_cli/
|
|
77
|
+
│ ├── __init__.py
|
|
78
|
+
│ ├── main.py # CLI entry point — Typer app, all commands
|
|
79
|
+
│ ├── detect.py # Project type auto-detection
|
|
80
|
+
│ ├── install.py # Template fetching and writing
|
|
81
|
+
│ └── templates/
|
|
82
|
+
│ ├── python/devsecops.yml # Embedded GitHub Actions templates
|
|
83
|
+
│ ├── nodejs/devsecops.yml
|
|
84
|
+
│ ├── go/devsecops.yml
|
|
85
|
+
│ └── gitlab/
|
|
86
|
+
│ ├── python/devsecops.yml # Embedded GitLab CI templates
|
|
87
|
+
│ ├── nodejs/devsecops.yml
|
|
88
|
+
│ └── go/devsecops.yml
|
|
89
|
+
├── templates/
|
|
90
|
+
│ ├── python/devsecops.yml # Source GitHub Actions templates
|
|
91
|
+
│ ├── nodejs/devsecops.yml # (must stay in sync with embedded copies above)
|
|
92
|
+
│ ├── go/devsecops.yml
|
|
93
|
+
│ ├── gitlab/
|
|
94
|
+
│ │ ├── python/devsecops.yml # Source GitLab CI templates
|
|
95
|
+
│ │ ├── nodejs/devsecops.yml
|
|
96
|
+
│ │ └── go/devsecops.yml
|
|
97
|
+
│ └── github/
|
|
98
|
+
│ └── publish-dashboard.yml # GitHub Pages dashboard workflow
|
|
99
|
+
├── dashboard/
|
|
100
|
+
│ ├── generate.py # SARIF → static HTML dashboard generator
|
|
101
|
+
│ └── template.html # Dashboard HTML/CSS template
|
|
102
|
+
├── policy/
|
|
103
|
+
│ ├── default.rego # OPA policy: block on CRITICAL
|
|
104
|
+
│ ├── strict.rego # OPA policy: block on HIGH + CRITICAL
|
|
105
|
+
│ └── permissive.rego # OPA policy: audit only, never block
|
|
106
|
+
├── docs/
|
|
107
|
+
│ ├── getting-started.md
|
|
108
|
+
│ ├── cli-reference.md
|
|
109
|
+
│ ├── pipeline-reference.md
|
|
110
|
+
│ ├── policy-reference.md
|
|
111
|
+
│ ├── dashboard-guide.md
|
|
112
|
+
│ ├── gitlab-guide.md
|
|
113
|
+
│ └── zh/ # Chinese documentation
|
|
114
|
+
├── tests/
|
|
115
|
+
│ ├── test_detect.py
|
|
116
|
+
│ ├── test_install.py
|
|
117
|
+
│ ├── test_dashboard.py
|
|
118
|
+
│ └── test_cli.py
|
|
119
|
+
├── pyproject.toml
|
|
120
|
+
├── README.md
|
|
121
|
+
├── README.zh.md
|
|
122
|
+
├── CONTRIBUTING.md
|
|
123
|
+
├── CHANGELOG.md
|
|
124
|
+
└── SECURITY.md
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Key Design Decisions
|
|
128
|
+
|
|
129
|
+
| Decision | Rationale |
|
|
130
|
+
|----------|-----------|
|
|
131
|
+
| Templates are **embedded** in the package (`src/cast_cli/templates/`) | Ensures `cast init` works offline and without network calls at install time |
|
|
132
|
+
| Templates also exist at **top-level** (`templates/`) | Allows direct `curl` access for manual installation without the CLI |
|
|
133
|
+
| **Auto-detection** before explicit `--type` | Reduces friction for the happy path |
|
|
134
|
+
| **`--force` flag** required to overwrite | Prevents accidental overwrites of customized workflows |
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Adding a New Template
|
|
139
|
+
|
|
140
|
+
To add support for a new language or framework, follow these steps:
|
|
141
|
+
|
|
142
|
+
### 1. Create the workflow template
|
|
143
|
+
|
|
144
|
+
Create the file at both paths (they must be identical):
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
templates/<stack>/devsecops.yml
|
|
148
|
+
src/cast_cli/templates/<stack>/devsecops.yml
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
A template must:
|
|
152
|
+
|
|
153
|
+
- Follow the CAST naming convention (`name: CAST DevSecOps`)
|
|
154
|
+
- Run on `push` and `pull_request` to `main`/`master`
|
|
155
|
+
- Include at minimum: secrets detection, SAST, and SCA
|
|
156
|
+
- Upload all SARIF results to GitHub Security tab
|
|
157
|
+
- Include a `gate` job that blocks merges on critical failures
|
|
158
|
+
|
|
159
|
+
Use the existing `templates/python/devsecops.yml` as a reference implementation.
|
|
160
|
+
|
|
161
|
+
### 2. Register the marker files
|
|
162
|
+
|
|
163
|
+
In `src/cast_cli/detect.py`, add your stack's marker files to the `MARKERS` dict.
|
|
164
|
+
Order matters — entries listed first win in monorepos:
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
MARKERS: dict[str, list[str]] = {
|
|
168
|
+
"go": ["go.mod"],
|
|
169
|
+
"nodejs": ["package.json"],
|
|
170
|
+
"python": ["pyproject.toml", "requirements.txt", "setup.py", "setup.cfg"],
|
|
171
|
+
"<stack>": ["<marker-file>"], # add your stack here
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### 3. Register the supported type
|
|
176
|
+
|
|
177
|
+
In `src/cast_cli/install.py`, add your stack to the `SUPPORTED` set:
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
SUPPORTED: set[str] = {"python", "nodejs", "go", "<stack>"}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### 4. Update documentation
|
|
184
|
+
|
|
185
|
+
- Add the new stack to the **Templates** table in `README.md`
|
|
186
|
+
- Add an entry in `CHANGELOG.md` under the appropriate version
|
|
187
|
+
- Add the stack description to `SUPPORTED_TYPES` in `main.py`
|
|
188
|
+
|
|
189
|
+
### 5. Test manually
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
# Create a temporary test directory with a marker file
|
|
193
|
+
mkdir /tmp/test-<stack> && cd /tmp/test-<stack>
|
|
194
|
+
touch <marker-file>
|
|
195
|
+
|
|
196
|
+
# Run cast init and verify the workflow is created
|
|
197
|
+
cast init
|
|
198
|
+
cat .github/workflows/devsecops.yml
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Running Tests
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
# Run all tests
|
|
207
|
+
pytest
|
|
208
|
+
|
|
209
|
+
# Run with coverage
|
|
210
|
+
pytest --cov=cast_cli
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
> New contributions should include tests for any new detection logic, CLI behavior,
|
|
214
|
+
> or template changes. The test suite covers `detect`, `install`, `dashboard`, and CLI.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Code Style
|
|
219
|
+
|
|
220
|
+
CAST uses [Ruff](https://docs.astral.sh/ruff/) for linting and formatting.
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
# Check for lint issues
|
|
224
|
+
ruff check src/
|
|
225
|
+
|
|
226
|
+
# Auto-fix issues
|
|
227
|
+
ruff check --fix src/
|
|
228
|
+
|
|
229
|
+
# Format code
|
|
230
|
+
ruff format src/
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
All code must pass `ruff check` with no errors before a pull request can be merged.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Submitting a Pull Request
|
|
238
|
+
|
|
239
|
+
1. **Create a feature branch** from `main`:
|
|
240
|
+
```bash
|
|
241
|
+
git checkout -b feat/nodejs-template
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
2. **Make your changes** following the guidelines in this document
|
|
245
|
+
|
|
246
|
+
3. **Test your changes** manually and with the test suite
|
|
247
|
+
|
|
248
|
+
4. **Update the changelog** in `CHANGELOG.md` under `[Unreleased]`
|
|
249
|
+
|
|
250
|
+
5. **Push and open a PR** against `main`:
|
|
251
|
+
```bash
|
|
252
|
+
git push origin feat/nodejs-template
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Pull Request Checklist
|
|
256
|
+
|
|
257
|
+
- [ ] Changes are limited to a single concern
|
|
258
|
+
- [ ] Code passes `ruff check` with no errors
|
|
259
|
+
- [ ] Manual testing completed
|
|
260
|
+
- [ ] `CHANGELOG.md` updated
|
|
261
|
+
- [ ] Documentation updated (README, docstrings)
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Release Process
|
|
266
|
+
|
|
267
|
+
Releases are managed by maintainers. The process:
|
|
268
|
+
|
|
269
|
+
1. Update `CHANGELOG.md` — move items from `[Unreleased]` to a new version section
|
|
270
|
+
2. Create and push a version tag: `git tag v0.x.0 && git push origin v0.x.0`
|
|
271
|
+
3. `setuptools-scm` reads the tag to set the package version automatically
|
|
272
|
+
4. The package is built and published to PyPI via CI
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Questions?
|
|
277
|
+
|
|
278
|
+
Open a [GitHub Discussion](https://github.com/castops/cast/discussions) or file an
|
|
279
|
+
[issue](https://github.com/castops/cast/issues).
|