mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 03:49:26 +00:00
feat(ops): a sourceable wrapper so docker is nobody's problem
source ops/shell/ousast.sh # bash / zsh
. ops\shell\ousast.ps1 # powershell
ousast scan .
ousast scan src/api
The container becomes an implementation detail. The engine is a JVM and ~2 GB
of Joern, and a contributor evaluating this tool should not have to install
either to try it -- what they get is a command.
Path rewriting is what makes it feel local: arguments under the working
directory are mapped to their mounted equivalents, so `src/api` means what the
user expects. Anything outside the mount is passed through unchanged WITH A
WARNING rather than silently rewritten into a path the container cannot see --
a wrong path that looks like it worked is worse than one that complains.
A native install wins when it is present. Someone who installed Joern
deliberately should not be quietly routed through a container, so the wrapper
uses a local joern-parse and venv when both exist; OUSAST_PREFER_NATIVE=0
forces the container. Verified end to end from an unrelated directory.
The image builds itself on first use and says why it is pausing. A first run
that prints a wall of build output and then works is worse than one that
explains itself.
Defaults stay conservative: the working directory mounts READ-ONLY, findings go
to ./.ousast as the only writable mount, and the network is off unless
`ousast-with-judge` asks for it.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01T7XD7x5KonMdmGRkp63EQG
This commit is contained in:
co-authored by
Claude Opus 5
parent
46232fe3e6
commit
adbb824eac
+2
-2
@@ -15,8 +15,8 @@ services:
|
|||||||
JOERN_VERSION: ${JOERN_VERSION:-v4.0.623}
|
JOERN_VERSION: ${JOERN_VERSION:-v4.0.623}
|
||||||
image: openultrasast:${OUSAST_VERSION:-dev}
|
image: openultrasast:${OUSAST_VERSION:-dev}
|
||||||
volumes:
|
volumes:
|
||||||
- ${TARGET:-.}:/target:ro # the repository under analysis, read-only
|
- ${TARGET:-.}:/target:ro # the repository under analysis, read-only
|
||||||
- ./reports:/reports # where findings land, the one writable mount
|
- ${OUSAST_REPORTS:-./reports}:/reports # where findings land, the one writable mount
|
||||||
working_dir: /work
|
working_dir: /work
|
||||||
tmpfs:
|
tmpfs:
|
||||||
# CPG construction is scratch-heavy and none of it should outlive the run or touch the host.
|
# CPG construction is scratch-heavy and none of it should outlive the run or touch the host.
|
||||||
|
|||||||
@@ -3,6 +3,37 @@
|
|||||||
Infrastructure for the optional engines. Nothing here is a project dependency: the core install is
|
Infrastructure for the optional engines. Nothing here is a project dependency: the core install is
|
||||||
`dependencies = []` and every engine below degrades to a recorded reason when it is absent.
|
`dependencies = []` and every engine below degrades to a recorded reason when it is absent.
|
||||||
|
|
||||||
|
## Use it as a command
|
||||||
|
|
||||||
|
Source the wrapper once and Docker stops being something you think about:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# ~/.bashrc or ~/.zshrc
|
||||||
|
source /path/to/OpenUltraSAST/ops/shell/ousast.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# $PROFILE
|
||||||
|
. C:\path\to\OpenUltraSAST\ops\shell\ousast.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, from any directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ousast scan .
|
||||||
|
ousast scan src/api
|
||||||
|
ousast-with-judge scan . # opt into the LLM judge: needs network and a key
|
||||||
|
```
|
||||||
|
|
||||||
|
The wrapper rewrites paths under your working directory to their mounted equivalents, so `src/api` means what
|
||||||
|
you expect. Anything outside the mount is passed through unchanged with a warning rather than silently
|
||||||
|
rewritten into a path the container cannot see. Your directory is mounted **read-only**; findings go to
|
||||||
|
`./.ousast`, the one writable mount. The image builds itself on first use and says so.
|
||||||
|
|
||||||
|
If `joern-parse` is already on your PATH and a local venv exists, the wrapper uses those instead — someone who
|
||||||
|
installed the engine deliberately should not be quietly routed through a container. Set
|
||||||
|
`OUSAST_PREFER_NATIVE=0` to always containerise.
|
||||||
|
|
||||||
## Two ways to get the engine
|
## Two ways to get the engine
|
||||||
|
|
||||||
**Docker (recommended for contributors).** The tool and Joern ship in one image, so nothing is installed on
|
**Docker (recommended for contributors).** The tool and Joern ship in one image, so nothing is installed on
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# OpenUltraSAST as a local command. Dot-source this from your PowerShell profile:
|
||||||
|
#
|
||||||
|
# . C:\path\to\OpenUltraSAST\ops\shell\ousast.ps1
|
||||||
|
#
|
||||||
|
# Then use it from any directory:
|
||||||
|
#
|
||||||
|
# ousast scan .
|
||||||
|
# ousast scan src\api
|
||||||
|
#
|
||||||
|
# Docker is an implementation detail. Your working directory is mounted READ-ONLY and findings are written
|
||||||
|
# to .\.ousast, the only writable mount.
|
||||||
|
|
||||||
|
$script:OusastHome = if ($env:OUSAST_HOME) { $env:OUSAST_HOME }
|
||||||
|
else { Split-Path -Parent (Split-Path -Parent $PSScriptRoot) }
|
||||||
|
|
||||||
|
function ousast {
|
||||||
|
$compose = Join-Path $script:OusastHome 'docker-compose.yml'
|
||||||
|
if (-not (Test-Path $compose)) {
|
||||||
|
Write-Error "ousast: cannot find $compose (set OUSAST_HOME to the checkout)"; return
|
||||||
|
}
|
||||||
|
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) {
|
||||||
|
Write-Error "ousast: docker is not installed"; return
|
||||||
|
}
|
||||||
|
|
||||||
|
$version = if ($env:OUSAST_VERSION) { $env:OUSAST_VERSION } else { 'dev' }
|
||||||
|
docker image inspect "openultrasast:$version" *> $null
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Host "ousast: building the analysis image once (~2 GB of engine; later runs are instant)"
|
||||||
|
Push-Location $script:OusastHome; docker compose build; Pop-Location
|
||||||
|
}
|
||||||
|
|
||||||
|
# Rewrite paths under the current directory to their mounted equivalent; leave anything outside alone
|
||||||
|
# and say so, rather than silently rewriting it into a path the container cannot see.
|
||||||
|
$cwd = (Get-Location).Path
|
||||||
|
$mapped = foreach ($a in $args) {
|
||||||
|
if (Test-Path $a) {
|
||||||
|
$abs = (Resolve-Path $a).Path
|
||||||
|
if ($abs -eq $cwd) { '/target' }
|
||||||
|
elseif ($abs.StartsWith($cwd)) { '/target' + $abs.Substring($cwd.Length).Replace('\', '/') }
|
||||||
|
else { Write-Warning "ousast: $a is outside $cwd and is not mounted"; $a }
|
||||||
|
} else { $a }
|
||||||
|
}
|
||||||
|
|
||||||
|
New-Item -ItemType Directory -Force -Path (Join-Path $cwd '.ousast') | Out-Null
|
||||||
|
$env:TARGET = $cwd
|
||||||
|
$env:OUSAST_REPORTS = Join-Path $cwd '.ousast'
|
||||||
|
if (-not $env:OUSAST_NETWORK) { $env:OUSAST_NETWORK = 'none' }
|
||||||
|
docker compose -f $compose run --rm ousast @mapped
|
||||||
|
}
|
||||||
|
|
||||||
|
# The LLM judge needs network and a key; opt in explicitly.
|
||||||
|
function ousast-with-judge { $env:OUSAST_NETWORK = 'bridge'; ousast @args }
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
# OpenUltraSAST as a local command. Source this from ~/.bashrc or ~/.zshrc:
|
||||||
|
#
|
||||||
|
# source /path/to/OpenUltraSAST/ops/shell/ousast.sh
|
||||||
|
#
|
||||||
|
# Then use it like any other linter, from any directory:
|
||||||
|
#
|
||||||
|
# ousast scan .
|
||||||
|
# ousast scan src/api
|
||||||
|
# ousast pairs --slice vibe-py
|
||||||
|
#
|
||||||
|
# Docker is an implementation detail on purpose. The engine is a JVM and ~2 GB of Joern, and a contributor
|
||||||
|
# evaluating this tool should not have to install either to try it. What they get instead is a command.
|
||||||
|
#
|
||||||
|
# The one thing worth knowing: your working directory is mounted READ-ONLY, and findings are written to
|
||||||
|
# ./.ousast, which is the only writable mount. A tool that reads your source to tell you about it has no
|
||||||
|
# business being able to change it.
|
||||||
|
|
||||||
|
# Resolve this script's own location so the function works from anywhere, under bash or zsh.
|
||||||
|
if [ -n "${BASH_SOURCE[0]:-}" ]; then
|
||||||
|
_OUSAST_HOME="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
elif [ -n "${(%):-%N}" ] 2>/dev/null; then
|
||||||
|
_OUSAST_HOME="$(cd "$(dirname "${(%):-%N}")/../.." && pwd)"
|
||||||
|
fi
|
||||||
|
export OUSAST_HOME="${OUSAST_HOME:-$_OUSAST_HOME}"
|
||||||
|
|
||||||
|
ousast() {
|
||||||
|
local compose="$OUSAST_HOME/docker-compose.yml"
|
||||||
|
if [ ! -f "$compose" ]; then
|
||||||
|
echo "ousast: cannot find $compose (set OUSAST_HOME to the checkout)" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# A native install wins when it is there: it is faster, and someone who installed Joern deliberately
|
||||||
|
# should not be quietly routed through a container.
|
||||||
|
if [ "${OUSAST_PREFER_NATIVE:-1}" = "1" ] && command -v joern-parse >/dev/null 2>&1 \
|
||||||
|
&& [ -x "$OUSAST_HOME/.venv/bin/python" ]; then
|
||||||
|
( cd "$OUSAST_HOME" && .venv/bin/python -m openultrasast.cli "$@" )
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v docker >/dev/null 2>&1; then
|
||||||
|
echo "ousast: docker is not installed, and no local joern-parse was found" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build once, silently, the first time. A first run that prints a wall of build output and then works is
|
||||||
|
# worse than one that pauses and explains itself.
|
||||||
|
if ! docker image inspect "openultrasast:${OUSAST_VERSION:-dev}" >/dev/null 2>&1; then
|
||||||
|
echo "ousast: building the analysis image once (this pulls ~2 GB of engine; later runs are instant)" >&2
|
||||||
|
( cd "$OUSAST_HOME" && docker compose build ) || return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Rewrite paths that live under the current directory to their mounted equivalent, so `ousast scan src/api`
|
||||||
|
# means what the user expects. Anything outside the mount is left alone and warned about rather than
|
||||||
|
# silently rewritten into a path the container cannot see.
|
||||||
|
local args=() a abs
|
||||||
|
for a in "$@"; do
|
||||||
|
if [ -e "$a" ]; then
|
||||||
|
abs="$(cd "$(dirname "$a")" 2>/dev/null && pwd)/$(basename "$a")"
|
||||||
|
case "$abs" in
|
||||||
|
"$PWD") args+=("/target") ;;
|
||||||
|
"$PWD"/*) args+=("/target${abs#"$PWD"}") ;;
|
||||||
|
*) echo "ousast: $a is outside $PWD and is not mounted; passing it through unchanged" >&2
|
||||||
|
args+=("$a") ;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
args+=("$a")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
mkdir -p "$PWD/.ousast"
|
||||||
|
TARGET="$PWD" OUSAST_REPORTS="$PWD/.ousast" \
|
||||||
|
OUSAST_NETWORK="${OUSAST_NETWORK:-none}" \
|
||||||
|
docker compose -f "$compose" run --rm ousast "${args[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# The LLM judge needs the network and a key; without them the model layer still entails and only the
|
||||||
|
# `suspicion` band goes unasked. This makes that opt-in explicit rather than a surprise.
|
||||||
|
ousast-with-judge() {
|
||||||
|
OUSAST_NETWORK=bridge ousast "$@"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user