Skip to content

Commit 789116e

Browse files
SimonGurneyQA Team
andauthored
extra tools (#72)
* extra tools * remove latex * size fixes * size fixes * tweaks * report fixes * fixes * add dedupe * fix temporario bug * fix temporario bug * add azure * add validation steps to report * awesome webs * fixes * openai fixes --------- Co-authored-by: QA Team <qa@bugcatcher.com>
1 parent bf34498 commit 789116e

53 files changed

Lines changed: 5987 additions & 1123 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build_pr.yml

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Build and publish release on new tag
1+
name: Build and publish PR image
22
on:
33
pull_request:
44
types: [opened, synchronize, reopened]
@@ -20,19 +20,11 @@ jobs:
2020
- name: Set up Docker Buildx
2121
id: buildx
2222
uses: docker/setup-buildx-action@v3
23-
- name: Build and push SAIST-Lite
24-
run: |
25-
docker buildx build . \
26-
--push \
27-
--progress=plain \
28-
--tag docker.io/punksecurity/saist:lite-pr-${{ github.event.number }} \
29-
--platform linux/amd64 \
30-
--target saist
31-
- name: Build and push SAIST
32-
run: |
33-
docker buildx build . \
34-
--push \
35-
--progress=plain \
36-
--tag docker.io/punksecurity/saist:pr-${{ github.event.number }} \
37-
--platform linux/amd64 \
38-
--target saist-tex
23+
- name: Build and push SAIST
24+
run: |
25+
docker buildx build . \
26+
--push \
27+
--progress=plain \
28+
--tag docker.io/punksecurity/saist:pr-${{ github.event.number }} \
29+
--platform linux/amd64 \
30+
--target saist

.github/workflows/build_release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,4 @@ jobs:
2828
VERSION: ${{ steps.version.outputs.version }}
2929
with:
3030
push: true
31-
targets: "full,lite"
31+
targets: "release"

.github/workflows/tests.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Tests
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened]
6+
7+
jobs:
8+
pytest:
9+
runs-on: ubuntu-latest
10+
11+
steps:
12+
- name: Check out code
13+
uses: actions/checkout@v4
14+
15+
- name: Set up Python
16+
uses: actions/setup-python@v5
17+
with:
18+
python-version: "3.12"
19+
cache: pip
20+
cache-dependency-path: |
21+
requirements.txt
22+
requirements-dev.txt
23+
24+
- name: Install dependencies
25+
run: |
26+
python -m pip install --upgrade pip
27+
pip install -r requirements-dev.txt
28+
29+
- name: Run pytest
30+
run: pytest -q

Dockerfile

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -19,43 +19,8 @@ COPY saist .
1919
# Exports
2020
ENV SAIST_COMMAND "docker run punksecurity/saist"
2121
ENV SAIST_CSV_PATH "/app/results.csv"
22-
ENV SAIST_TEX_FILENAME "report.tex"
2322
ENV SAIST_PDF_FILENAME "report.pdf"
2423
ENV SAIST_WEB_HOST "0.0.0.0"
2524
ENV PYTHONUNBUFFERED 1
2625
ENTRYPOINT [ "python3", "/app/main.py" ]
2726
CMD [ "-h" ]
28-
29-
FROM alpine as tex-dl
30-
WORKDIR /tmp
31-
RUN mkdir -p /opt/texlive/bin
32-
33-
RUN wget https://ftp.math.utah.edu/pub/texlive-utah/bin/aarch64-alpine322.tar.xz && \
34-
tar xvf aarch64-alpine322.tar.xz && ls -ltra && \
35-
mv aarch64-alpine322 /opt/texlive/bin/aarch64-linuxmusl
36-
37-
RUN wget https://ftp.math.utah.edu/pub/texlive-utah/bin/x86_64-alpine322.tar.xz && \
38-
tar xvf x86_64-alpine322.tar.xz && \
39-
mv x86_64-alpine322 /opt/texlive/bin/x86_64-linuxmusl
40-
41-
FROM saist AS saist-tex
42-
43-
ARG TL_MIRROR="https://texlive.info/CTAN/systems/texlive/tlnet"
44-
45-
COPY saist/latex/texlive.profile /tmp
46-
COPY --from=tex-dl /opt/texlive/bin /tmp/texlive/bin
47-
RUN apk add --no-cache perl curl fontconfig xz && \
48-
mkdir -p "/tmp/texlive" && cd "/tmp/texlive" && \
49-
wget "$TL_MIRROR/install-tl-unx.tar.gz" && \
50-
tar xzvf ./install-tl-unx.tar.gz && \
51-
"./install-tl-"*"/install-tl" --location "$TL_MIRROR" --custom-bin=/tmp/texlive/bin/$(uname -m)-linuxmusl -profile "/tmp/texlive.profile" && \
52-
rm -vf "/opt/texlive/install-tl" && \
53-
rm -vf "/opt/texlive/install-tl.log" && \
54-
rm -vrf /tmp/*
55-
56-
ENV PATH="${PATH}:/opt/texlive/bin/custom"
57-
58-
ARG TL_PACKAGES="lineno titlesec upquote minted blindtext booktabs fontawesome latexmk parskip xcolor"
59-
60-
RUN tlmgr update --self && \
61-
tlmgr install ${TL_PACKAGES}

README.md

Lines changed: 73 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@ We support OLLAMA for local / offline code scanning.
2929
- **Diff scanning**: Git commits, branches, or PRs
3030
- **Multi-LLM support**: `OpenAI`, `Anthropic`, `Bedrock`, `DeepSeek`, `Gemini`, `Ollama`
3131
- **Filesystem, Git, GitHub PR scanning modes**
32-
- **Pattern-based file inclusion/exclusion** using `.saist.include` and `.saist.ignore`
33-
- **Interactive chat** with your findings
34-
- **Web server** UI to view results
35-
- **CSV export** of findings
32+
- **Pattern-based file inclusion/exclusion** using `.saist.include` and `.saist.ignore`
33+
- **Project-specific analysis skills** loaded from Markdown files to teach SAIST app routing, authorization, framework conventions, and other local security context
34+
- **LLM-generated analysis skills** for bootstrapping those files in a separate run
35+
- **Interactive chat** with your findings
36+
- **Web server** UI to view results
37+
- **CSV export** of findings
3638
- **PDF report**: Generate PDF reports of SAIST findings
3739
- **CI/CD pipeline friendly** (exit 1 on findings)
3840

@@ -73,16 +75,18 @@ export SAIST_LLM_API_KEY=your-api-key
7375

7476
| Task | Command |
7577
|:-----|:--------|
76-
| Get a DevSecOps poem | `saist/main.py --llm openai poem` |
77-
| Scan a local folder | `saist/main.py --llm deepseek filesystem /path/to/code` |
78-
| Scan a local folder with ollama from within docker| `docker run --network=host -v <folder_path>:/vulnerableapp -v $PWD/reporting:/app/reporting punksecurity/saist --llm ollama --llm-model gemma3:4b fileystem /vulnerableapp` |
78+
| Get a DevSecOps poem | `saist/main.py --llm openai poem` |
79+
| Scan a local folder | `saist/main.py --llm deepseek filesystem /path/to/code` |
80+
| Scan a local folder file-by-file | `saist/main.py --llm deepseek --deep filesystem /path/to/code` |
81+
| Scan a local folder with ollama from within docker| `docker run --network=host -v <folder_path>:/vulnerableapp -v $PWD/reporting:/app/reporting punksecurity/saist --llm ollama --llm-model gemma3:4b fileystem /vulnerableapp` |
7982
| Scan a local Git repo | `saist/main.py --llm openai git /path/to/repo` |
8083
| Scan a local Git repo (branch diff) | `saist/main.py --llm openai git /path/to/repo --ref-for-compare main --ref-to-compare feature-branch` |
8184
| Scan a GitHub PR (and update the PR) | `saist/main.py --llm anthropic github yourorg/yourrepo 1234 --github-token your-token` |
8285
| Launch web server to view findings | `saist/main.py --llm deepseek --web filesystem /path/to/code` |
8386
| Interactive shell after scanning | `saist/main.py --llm ollama --interactive filesystem /path/to/code` |
84-
| Export findings as CSV | `saist/main.py --llm openai --csv filesystem /path/to/code` |
85-
| Scan with docker and export findings as PDF report | `docker run -v <folder_path>:/vulnerableapp -v $PWD/reporting:/app/reporting punksecurity/saist --llm openai --pdf filesystem /vulnerableapp` |
87+
| Export findings as CSV | `saist/main.py --llm openai --csv filesystem /path/to/code` |
88+
| Generate analysis skills | `saist/main.py --llm openai --generate-skills filesystem /path/to/code` |
89+
| Scan with docker and export findings as PDF report | `docker run -v <folder_path>:/vulnerableapp -v $PWD/reporting:/app/reporting punksecurity/saist --llm openai --pdf filesystem /vulnerableapp` |
8690
| Scan with docker and export findings as PDF report with a project title | `docker run -v <folder_path>:/vulnerableapp -v $PWD/reporting:/app/reporting punksecurity/saist --llm openai --pdf --project-name "Project Name" filesystem /vulnerableapp` |
8791
| Scan with docker and retain cache for future runs | `docker run -v <folder_path>:/vulnerableapp -v $PWD/SAISTCache:/app/SAISTCache punksecurity/saist --llm openai filesystem /vulnerableapp` |
8892
| Change caching folder | `saist/main.py --llm openai --cache-folder /path/to/cache filesystem /path/to/code` |
@@ -106,7 +110,7 @@ saist respects **file include/exclude rules** via two optional files in the root
106110
- `build/` will ignore the entire build folder
107111
- `*.log` will ignore all log files
108112

109-
You can also provide include/exclude patterns using the command-line arguments `--include` and `--exclude`.
113+
You can also provide include/exclude patterns using the command-line arguments `--include` and `--exclude`.
110114
- Patterns provided via command-line arguments are appended to any patterns loaded from the rule files.
111115
- Examples:
112116
- `--include '**/*.py' --include '**/*.ts'` includes all Python and TypeScript files
@@ -132,23 +136,51 @@ docs/
132136
This setup will:
133137
- Only scan `.py`, `.ts`, and specific `.js` files
134138
- Ignore anything under `tests/` and `docs/`
135-
---
136-
137-
138-
## 📄 PDF report generation
139+
---
140+
141+
## 🧠 Analysis Skills
142+
143+
SAIST can load project-specific analysis skill files from `.saist/skills/*.md`. These files are added to the security review prompt so future scans understand application-specific details such as routing, authentication, authorization, framework conventions, data access, validation boundaries, dependencies, configuration, and security-sensitive workflows.
144+
145+
Generate an initial set of skill files as a separate run:
146+
147+
```bash
148+
saist/main.py --llm openai --generate-skills filesystem /path/to/code
149+
```
150+
151+
Then review or edit the generated Markdown files and run SAIST normally. Skill files are loaded automatically on future scans:
152+
153+
```bash
154+
saist/main.py --llm openai filesystem /path/to/code
155+
```
156+
157+
Useful options:
158+
159+
| Option | Description |
160+
|:------|:------------|
161+
| `--skills-path` | Folder containing skill Markdown files. Defaults to `.saist/skills` under the scanned project. |
162+
| `--generate-skills` | Ask the configured LLM to generate skill files and then exit. |
163+
| `--overwrite-skills` | Replace existing skill files during generation. Without this, existing files are preserved. |
164+
| `--disable-skills` | Do not load skill files during analysis. |
165+
| `--skills-max-bytes` | Limit total skill guidance added to analysis prompts. |
166+
| `--skills-sample-files` / `--skills-sample-bytes` | Control how much project context is sampled when generating skills. |
167+
168+
When skills are loaded, SAIST salts its findings cache with the skill content so updated guidance gets a fresh analysis run.
169+
170+
---
171+
172+
173+
## 📄 PDF report generation
139174

140175
saist allows you to generate PDF reports summarizing your findings, making it easier to share insights with your team.
141176

142-
To create a PDF report, simply use the `--pdf` flag when running the scan. By default, the report will be saved to
143-
`reporting/report.pdf`. You can customize the filename by using the `--pdf-filename` option followed by your desired
144-
filename.
145-
146-
To add a project name onto the title page of the PDF report, use the `--project-name` option followed by your desired title.
147-
148-
> It is recommended to use the provided Docker image for generating PDF reports, as it includes the necessary TeX suite,
149-
which can be quite large. This ensures that all dependencies are met and the report is generated properly.
150-
151-
If not, you need to install latexmk to make it work.
177+
To create a PDF report, use the `--pdf` flag when running the scan. By default, the report will be saved to
178+
`reporting/report.pdf`. You can customize the filename by using the `--pdf-filename` option followed by your desired
179+
filename.
180+
181+
To add a project name onto the title page of the PDF report, use the `--project-name` option followed by your desired title.
182+
183+
PDF reports are generated with the built-in ReportLab renderer, so no external document-rendering toolchain is required.
152184

153185
### 🐋 Example (Docker)
154186

@@ -169,13 +201,23 @@ docker run -v$PWD/code:/code -v$PWD/reporting:/app/reporting punksecurity/saist
169201

170202
| Option | Description |
171203
|:------|:------------|
172-
| `--llm` | Select LLM (`anthropic`, `deepseek`, `gemini`, `ollama`, `openai`) |
173-
| `--llm-api-key` | API key for your LLM |
174-
| `--llm-model` | (Optional) Specific model (e.g., `gpt-4o`) |
175-
| `--interactive` | Chat with the LLM after scan |
176-
| `--web` | Launch a local web server |
177-
| `--disable-tools` | Disable tool use during file analysis to reduce LLM token usage |
178-
| `--disable-caching` | Disable finding caching during file analysis |
204+
| `--llm` | Select LLM (`anthropic`, `azure-foundry`, `bedrock`, `deepseek`, `gemini`, `ollama`, `openai`) |
205+
| `--llm-api-key` | API key for your LLM |
206+
| `--llm-model` | (Optional) Specific model (e.g., `gpt-4o`) |
207+
| `--thinking` | Pydantic AI thinking effort: `minimal`, `low`, `medium`, `high`, `xhigh`, or `disabled` |
208+
| `--openai-base-uri` | Base URI for OpenAI-compatible services. Can also be set with `SAIST_OPENAI_BASE_URI`. |
209+
| `--azure-openai-endpoint` | Azure AI Foundry or Azure OpenAI endpoint. Can also be set with `AZURE_OPENAI_ENDPOINT`; `/openai/v1/` endpoints use the Responses API without `api-version`. |
210+
| `--azure-openai-api-version` | Azure OpenAI API version for non-v1 endpoints. Can also be set with `OPENAI_API_VERSION`. |
211+
| `--interactive` | Chat with the LLM after scan |
212+
| `--web` | Launch a local web server |
213+
| `--disable-tools` | Disable tool use during file analysis to reduce LLM token usage |
214+
| `--deep` | For filesystem scans, analyze every file individually. Without this, filesystem scans send a file inventory and let the LLM inspect files with tools, then report file coverage. |
215+
| `--iterations` | Number of tool-driven filesystem scan passes to run when `--deep` is not set. Defaults to `1`; concurrency is capped by `--llm-rate-limit`. |
216+
| `--skills-path` | Folder containing SAIST analysis skill Markdown files |
217+
| `--generate-skills` | Generate SAIST analysis skill files and exit |
218+
| `--overwrite-skills` | Replace existing skill files during skill generation |
219+
| `--disable-skills` | Do not load skill files during analysis |
220+
| `--disable-caching` | Disable finding caching during file analysis |
179221
| `--skip-line-length-check` | Skip checking files for a maximum line length |
180222
| `--max-line-length` | Maximum allowed line length, files with lines longer than this value will be skipped |
181223
| `--i, --include` | Pattern to explicitly include |

docker-bake.hcl

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,11 @@ target "base" {
1111
target "nightly" {
1212
inherits = ["base"]
1313
tags = ["docker.io/punksecurity/saist:nightly"]
14-
}
15-
16-
target "lite" {
17-
inherits = ["base"]
18-
tags = ["docker.io/punksecurity/saist:lite-${VERSION}", "docker.io/punksecurity/saist:lite-latest"]
1914
target = "saist"
2015
}
2116

22-
target "full" {
17+
target "release" {
2318
inherits = ["base"]
2419
tags = ["docker.io/punksecurity/saist:${VERSION}", "docker.io/punksecurity/saist:latest"]
25-
target = "saist-tex"
20+
target = "saist"
2621
}

pytest.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[pytest]
2+
pythonpath = saist
3+
testpaths = tests

requirements-dev.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-r requirements.txt
2+
pytest==8.3.5

requirements.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
pydantic==2.11.7
1+
pydantic==2.12.5
22
requests==2.32.4
33
GitPython==3.1.44
44
python-dotenv==1.1.1
5-
pydantic-ai==0.3.7
5+
pydantic-ai-slim[anthropic,bedrock,google,openai]==1.93.0
66
aiofiles==24.1.0
77
rich==14.0.0
88
flask==3.1.1
99
ollama==0.5.1
1010
gitignore-parser==0.1.13
11+
reportlab==4.5.0

saist/latex/__init__.py

Lines changed: 0 additions & 88 deletions
This file was deleted.

0 commit comments

Comments
 (0)