Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
e6d925f
Add skills
Saloed Mar 30, 2026
a4977b0
refactor(cli): rename agent command to dev, move debug flags into dev…
Gr-i-niy May 8, 2026
a4814a5
refactor(skills): fix skills formatting, typos, other small issues
Gr-i-niy May 8, 2026
f892f0f
Fix Makefile
misonijnik May 14, 2026
91ed63c
feat: update all skills, introduce new workflow, new cli commands
Gr-i-niy May 27, 2026
d158c60
feat: update discovering skill
Gr-i-niy May 27, 2026
a3a6558
feat: add fallback for rule-creation process
Gr-i-niy May 27, 2026
e2c7775
feat: improve discovery workflow, refactor cli
Gr-i-niy Jun 2, 2026
4515a84
feat: modify rule creation process
Gr-i-niy Jun 2, 2026
7b1610d
fix: a few more fixes
Gr-i-niy Jun 4, 2026
957f104
fix: gitignore
Gr-i-niy Jun 4, 2026
be96c68
feat: add some improvements
Gr-i-niy Jun 4, 2026
670fd6c
fix: improve approximations writing, add instructions about stored in…
Gr-i-niy Jun 5, 2026
5492e86
fix(cli): resolve bundled lib/jre for FHS install layout
misonijnik Jun 6, 2026
9c27966
fix(cli): show bundled artifacts as "custom" not a nominal version
misonijnik Jun 6, 2026
6f3c34b
docs: document agent skills and CLI tooling
misonijnik Jun 8, 2026
96f4aa6
clean(docs): Clean tmp files
misonijnik Jun 8, 2026
17c2c77
fix(cli): avoid generated jar requirement in go build
misonijnik Jun 8, 2026
59a0227
fix(cli): check file Close errors in copyFile
misonijnik Jun 8, 2026
a20dc53
refactor(cli): dedupe path/scaffold helpers and clarify naming
misonijnik Jun 8, 2026
41107ca
refactor(cli): reuse parent directory helper
misonijnik Jun 8, 2026
c5077cf
fix(cli): clarify new command descriptions
misonijnik Jun 8, 2026
a278b1f
docs: clarify appsec-agent description
misonijnik Jun 8, 2026
808c205
refactor(cli): resolve artifact jar paths via one override-aware reso…
misonijnik Jun 9, 2026
9a7083b
refactor(cli): extract ScanConfig and remove reachability's global co…
misonijnik Jun 9, 2026
798d3c8
feat(cli): render health report as a nested tree like scan
misonijnik Jun 9, 2026
fa2092a
refactor(cli): share artifact version label across scan/compile/health
misonijnik Jun 9, 2026
85b31a5
fix(cli): drop path duplication in health's custom artifact version
misonijnik Jun 9, 2026
763d7ea
style(cli): drop trailing colon from tree node labels
misonijnik Jun 9, 2026
728563c
refactor(cli): dedupe rule/approximation plumbing into shared helpers
misonijnik Jun 9, 2026
e8d2005
docs: document npm/npx install across README and docs
misonijnik Jun 10, 2026
9c2694e
feat: add script for discovering package usages
Gr-i-niy Jun 9, 2026
6746b55
feat: replace python script with jar for better methods usage detection
Gr-i-niy Jun 9, 2026
e600896
fix(cli): qualify bare same-file join refs in rule expansion
misonijnik Jun 10, 2026
33a03d7
feat(cli): parse analyzer test-result.json in internal/analyzer
misonijnik Jun 10, 2026
c9f2a18
fix(cli): fail test runs on failing samples, reuse internal/analyzer …
misonijnik Jun 10, 2026
55f5f6d
fix(cli): keep release versions for marker-verified bundled installs,…
misonijnik Jun 10, 2026
d61ffd2
fix(cli): health exits non-zero on missing components and reports the…
misonijnik Jun 10, 2026
46cf09c
refactor(cli): single ArtifactDef-driven jar provisioning and shared …
misonijnik Jun 10, 2026
40de354
fix(cli): bind scan config keys to the executing command so explicit …
misonijnik Jun 10, 2026
90d9aad
fix(cli): carry all scan flags into suggestions, simplify scan plan a…
misonijnik Jun 10, 2026
4e40fa4
refactor(cli): extract test-project scaffolding into internal/testpro…
misonijnik Jun 10, 2026
5bc1fe8
fix(build): correct make dependency graph, install paths, and dev wra…
misonijnik Jun 10, 2026
0c5d8b3
fix(ci): ship test-util jar and release-version marker in CLI releases
misonijnik Jun 10, 2026
3e5a1c0
fix(skills): harden sarif-to-findings against block-style hashes, nul…
misonijnik Jun 10, 2026
7768b1b
docs: fix rule-test invocations to include marker and builtin rulesets
misonijnik Jun 10, 2026
6e5a24e
fix(skills): align approximation guidance, tracking schema, and scrip…
misonijnik Jun 10, 2026
7457438
fix(cli): surface jar-path overrides in pull summary
misonijnik Jun 10, 2026
6d27bfb
feat: improve searching package usages, fix small issues
Gr-i-niy Jun 10, 2026
627382b
feat: add npm installation to skill
Gr-i-niy Jun 10, 2026
7fd7127
refactor: strip added comments from skills branch code
misonijnik Jun 10, 2026
b6cb1d7
refactor: unify version marker name to .versions across tiers
misonijnik Jun 10, 2026
5786392
docs: drop experimental label from test command
misonijnik Jun 10, 2026
8c92dfb
fix: drop fragile literal lib/.versions goreleaser entry
misonijnik Jun 10, 2026
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/workflows/release-cli.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,29 @@ jobs:
mkdir -p lib/rules
tar -xzf /tmp/opentaint-rules.tar.gz -C lib/rules

cp internal/globals/versions.yaml lib/.versions
if [ ! -f lib/.versions ]; then
echo "::error::lib/.versions was not created; the bundled release would ship without a version marker" >&2
exit 1
fi

echo "Bundled artifacts:"
ls -la lib/
-
name: Set up JDK for test-util jar
if: ${{ steps.release_version.outputs.status == 'succeeded' }}
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'
-
name: Build and embed test-util jar
if: ${{ steps.release_version.outputs.status == 'succeeded' }}
run: |
set -euo pipefail
(cd core && ./gradlew :opentaint-sast-test-util:jar)
(cd cli && go generate ./...)
cp core/opentaint-sast-test-util/build/libs/opentaint-sast-test-util.jar cli/lib/
-
name: Run GoReleaser
if: ${{ steps.release_version.outputs.status == 'succeeded' }}
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ config.local.*

**/.gradle
**/build
/install/
core/**/bin/

# Ignore all hidden files and directories
.*
Expand Down
70 changes: 70 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
MAKE ?= make
GRADLEW := $(CURDIR)/core/gradlew
INSTALL ?= install

PREFIX ?= /usr/local
BINDIR ?= $(PREFIX)/bin
LIBDIR ?= $(PREFIX)/lib

CORE_DIR := core
CLI_DIR := cli

CLI_BINARY_NAME := opentaint
CLI_DEV_BINARY_NAME := opentaint-dev
ANALYZER_TASK := :projectAnalyzerJar
AUTOBUILDER_TASK := opentaint-jvm-autobuilder:projectAutoBuilderJar
TEST_UTIL_TASK := :opentaint-sast-test-util:jar

ANALYZER_JAR := $(CORE_DIR)/build/libs/opentaint-project-analyzer.jar
AUTOBUILDER_JAR := $(CORE_DIR)/opentaint-jvm-autobuilder/build/libs/opentaint-project-auto-builder.jar
TEST_UTIL_JAR := $(CORE_DIR)/opentaint-sast-test-util/build/libs/opentaint-sast-test-util.jar
RULES_SRC := rules/ruleset
INSTALLED_ANALYZER_JAR := $(LIBDIR)/$(notdir $(ANALYZER_JAR))
INSTALLED_AUTOBUILDER_JAR := $(LIBDIR)/$(notdir $(AUTOBUILDER_JAR))
INSTALLED_RULES_DIR := $(LIBDIR)/rules
INSTALLED_CLI_BINARY := $(BINDIR)/$(CLI_BINARY_NAME)
INSTALLED_DEV_BINARY := $(BINDIR)/$(CLI_DEV_BINARY_NAME)

.PHONY: all core projectAnalyzerJar core/autobuilder core/opentaint-sast-test-util cli install clean

all: core cli

core:
cd $(CORE_DIR) && $(GRADLEW) $(ANALYZER_TASK) $(AUTOBUILDER_TASK) $(TEST_UTIL_TASK)

projectAnalyzerJar:
cd $(CORE_DIR) && $(GRADLEW) $(ANALYZER_TASK)

core/autobuilder:
cd $(CORE_DIR) && $(GRADLEW) $(AUTOBUILDER_TASK)

core/opentaint-sast-test-util:
cd $(CORE_DIR) && $(GRADLEW) $(TEST_UTIL_TASK)

cli: core/opentaint-sast-test-util
$(MAKE) -C $(CLI_DIR) build

install: core
mkdir -p $(BINDIR) $(LIBDIR)
$(MAKE) -C $(CLI_DIR) install PREFIX=$(PREFIX) BINDIR=$(abspath $(BINDIR))
$(INSTALL) -m 0644 $(ANALYZER_JAR) $(INSTALLED_ANALYZER_JAR)
$(INSTALL) -m 0644 $(AUTOBUILDER_JAR) $(INSTALLED_AUTOBUILDER_JAR)
$(INSTALL) -m 0644 $(TEST_UTIL_JAR) $(LIBDIR)/$(notdir $(TEST_UTIL_JAR))
rm -rf $(INSTALLED_RULES_DIR)
mkdir -p $(INSTALLED_RULES_DIR)
cp -R $(RULES_SRC)/. $(INSTALLED_RULES_DIR)/
printf '%s\n' \
'#!/bin/sh' \
'set -eu' \
'if command -v realpath >/dev/null 2>&1; then SELF=$$(realpath "$$0"); else SELF=$$0; fi' \
'BIN_DIR=$$(CDPATH= cd -- "$$(dirname -- "$$SELF")" && pwd -P)' \
'PREFIX_DIR=$$(CDPATH= cd -- "$$BIN_DIR/.." && pwd)' \
'LIB_DIR="$$PREFIX_DIR/lib"' \
'exec "$$BIN_DIR/$(CLI_BINARY_NAME)" --experimental --analyzer-jar "$$LIB_DIR/$(notdir $(ANALYZER_JAR))" --autobuilder-jar "$$LIB_DIR/$(notdir $(AUTOBUILDER_JAR))" "$$@"' \
> $(INSTALLED_DEV_BINARY)
chmod 0755 $(INSTALLED_DEV_BINARY)
$(INSTALLED_CLI_BINARY) pull

clean:
$(MAKE) -C $(CLI_DIR) clean
cd $(CORE_DIR) && $(GRADLEW) clean
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,16 @@ brew install --cask seqra/tap/opentaint
irm https://opentaint.org/install.ps1 | iex
```

**Install via npm (Linux/macOS/Windows):**
```bash
npm install -g @seqra/opentaint
```

**Or run instantly with npx — no install required (needs Node.js):**
```bash
npx @seqra/opentaint scan
```

**Scan your project:**
```bash
opentaint scan
Expand All @@ -141,6 +151,24 @@ For more options, see [Installation](docs/README.md#installation) and [Usage](do

---

## AI Agent Workflows

OpenTaint includes agent skills that turn static analysis into an end-to-end application-security workflow. Install them with:

```bash
npx skills add https://github.com/seqra/opentaint
```

The `appsec-agent` skill orchestrates a full project assessment: build the project, run OpenTaint, discover the attack surface, add targeted rules, model missing library data flows, triage findings, and optionally generate dynamic proof-of-concept checks for confirmed vulnerabilities.

Included skills cover the common security-analysis loop:

- **Scan and triage:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc`
- **Coverage expansion:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules`
- **Dataflow modeling:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue`

---

## Documentation

Full guides — installation, usage, configuration, CI/CD integration: **[Documentation](docs/README.md)**.
Expand Down
2 changes: 1 addition & 1 deletion cli/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ bin/
!npm/bin/
dist/
dist-npm/
lib/
/lib/
/opentaint

# Operating system files
Expand Down
27 changes: 27 additions & 0 deletions cli/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
GO ?= go

BINARY_NAME ?= opentaint
BUILD_DIR ?= bin
BINARY_PATH := $(BUILD_DIR)/$(BINARY_NAME)

PREFIX ?= /usr/local
BINDIR ?= $(PREFIX)/bin
INSTALL_GOBIN := $(abspath $(BINDIR))

.PHONY: all generate build install clean

all: build

generate:
$(GO) generate ./...

build: generate
mkdir -p $(BUILD_DIR)
$(GO) build -o $(BINARY_PATH) .

install: build
mkdir -p $(INSTALL_GOBIN)
install -m 0755 $(BINARY_PATH) $(INSTALL_GOBIN)/$(BINARY_NAME)

clean:
rm -f $(BINARY_PATH)
22 changes: 22 additions & 0 deletions cli/cmd/analyzer_inputs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package cmd

import (
"github.com/seqra/opentaint/internal/utils/log"
)

func addDataflowApproximations(b *AnalyzerBuilder, paths []string, analyzerJarPath, projectModelDir string) {
for _, approxPath := range paths {
absApproxPath := log.AbsPathOrExit(approxPath, "dataflow-approximations")
compiledPath, err := compileApproximationsIfNeeded(absApproxPath, analyzerJarPath, projectModelDir)
if err != nil {
out.Fatalf("Approximation compilation failed: %s", err)
}
b.AddDataflowApproximations(compiledPath)
}
}

func addPassthroughApproximations(b *AnalyzerBuilder, paths []string) {
for _, passthrough := range paths {
b.AddPassthroughApproximations(log.AbsPathOrExit(passthrough, "passthrough-approximations"))
}
}
28 changes: 28 additions & 0 deletions cli/cmd/artifacts.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,36 @@ import (
"errors"
"fmt"
"os"

"github.com/seqra/opentaint/internal/globals"
"github.com/seqra/opentaint/internal/utils"
)

func ensureArtifactJar(def globals.ArtifactDef) (string, error) {
path, err := utils.ResolveJarPath(def)
if err != nil {
return "", fmt.Errorf("failed to construct path to the %s: %w", def.Kind(), err)
}
if def.Override != "" {
return path, nil
}

if err := ensureArtifactAvailable(def.Kind(), def.Version, path, func() error {
return utils.DownloadGithubReleaseAsset(globals.Config.Owner, globals.Config.Repo, def.Version, def.AssetName, path, globals.Config.Github.Token, globals.Config.SkipVerify, out)
}); err != nil {
return "", err
}
return path, nil
}

func ensureAnalyzerAvailable() (string, error) {
return ensureArtifactJar(globals.ArtifactByKind("analyzer"))
}

func ensureAutobuilderAvailable() (string, error) {
return ensureArtifactJar(globals.ArtifactByKind("autobuilder"))
}

func ensureArtifactAvailable(name, version, artifactPath string, download func() error) error {
if _, err := os.Stat(artifactPath); err == nil {
return nil
Expand Down
68 changes: 44 additions & 24 deletions cli/cmd/command_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,26 +42,28 @@ func NewAutobuilderBuilder() *AutobuilderBuilder {

type AnalyzerBuilder struct {
*BaseCommandBuilder
projectPath string
outputDir string
sarifFileName string
sarifCodeFlowLimit int64
sarifToolVersion string
sarifToolSemanticVersion string
sarifUriBase string
semgrepCompatibility bool
partialFingerprints bool
ifdsAnalysisTimeout int64
severities []string
ruleSetPaths []string
ruleLoadTracePath string
jarPath string
maxMemory string
ruleIDs []string
approximationsConfig []string
dataflowApproximations []string
trackExternalMethods bool
debugFactReachabilitySarif bool
projectPath string
outputDir string
sarifFileName string
sarifCodeFlowLimit int64
sarifToolVersion string
sarifToolSemanticVersion string
sarifUriBase string
semgrepCompatibility bool
partialFingerprints bool
ifdsAnalysisTimeout int64
severities []string
ruleSetPaths []string
ruleLoadTracePath string
jarPath string
maxMemory string
ruleIDs []string
passthroughApproximations []string
dataflowApproximations []string
trackExternalMethods bool
debugFactReachabilitySarif bool
runRuleTests bool
debugRunAnalysisOnSelectedEntryPoints string
}

func (a *AnalyzerBuilder) SetProject(projectPath string) *AnalyzerBuilder {
Expand Down Expand Up @@ -144,8 +146,8 @@ func (a *AnalyzerBuilder) AddRuleID(ruleID string) *AnalyzerBuilder {
return a
}

func (a *AnalyzerBuilder) AddApproximationsConfig(configPath string) *AnalyzerBuilder {
a.approximationsConfig = append(a.approximationsConfig, configPath)
func (a *AnalyzerBuilder) AddPassthroughApproximations(path string) *AnalyzerBuilder {
a.passthroughApproximations = append(a.passthroughApproximations, path)
return a
}

Expand All @@ -164,6 +166,16 @@ func (a *AnalyzerBuilder) EnableDebugFactReachabilitySarif() *AnalyzerBuilder {
return a
}

func (a *AnalyzerBuilder) SetDebugRunAnalysisOnSelectedEntryPoints(entryPoints string) *AnalyzerBuilder {
a.debugRunAnalysisOnSelectedEntryPoints = entryPoints
return a
}

func (a *AnalyzerBuilder) EnableRunRuleTests() *AnalyzerBuilder {
a.runRuleTests = true
return a
}

func (a *AnalyzerBuilder) BuildNativeCommand() []string {
// For native execution, create a temporary logs directory
tempLogsDir, err := os.MkdirTemp("", "opentaint-*")
Expand Down Expand Up @@ -237,8 +249,8 @@ func (a *AnalyzerBuilder) BuildNativeCommand() []string {
flags = append(flags, "--semgrep-rule-id", ruleID)
}

for _, configPath := range a.approximationsConfig {
flags = append(flags, "--approximations-config", configPath)
for _, passthrough := range a.passthroughApproximations {
flags = append(flags, "--passthrough-approximations", passthrough)
}

for _, approxPath := range a.dataflowApproximations {
Expand All @@ -253,6 +265,14 @@ func (a *AnalyzerBuilder) BuildNativeCommand() []string {
flags = append(flags, "--debug-fact-reachability-sarif")
}

if a.debugRunAnalysisOnSelectedEntryPoints != "" {
flags = append(flags, "--debug-run-analysis-on-selected-entry-points", a.debugRunAnalysisOnSelectedEntryPoints)
}

if a.runRuleTests {
flags = append(flags, "--debug-run-rule-tests")
}

return append(command, flags...)
}

Expand Down
27 changes: 2 additions & 25 deletions cli/cmd/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ Arguments:
}
sb.FieldNode("Project", absProjectRoot).
FieldNode("Output project model", absOutputProjectModelPath).
FieldNode("Autobuilder", utils.ArtifactDisplayVersion(globals.ArtifactByKind("autobuilder"), globals.Config.Autobuilder.JarPath)).
FieldNode("Autobuilder", utils.ArtifactVersionWithPath(globals.ArtifactByKind("autobuilder"))).
Render()

if DryRunCompile {
Expand All @@ -88,11 +88,7 @@ Arguments:
out.Fatalf("Native compile preparation failed: %s", err)
}

compileJavaRunner := java.NewJavaRunner().
WithSkipVerify(globals.Config.SkipVerify).
WithDebugOutput(out.DebugStream("Autobuilder")).
TrySystem().
TrySpecificVersion(globals.Config.Java.Version)
compileJavaRunner := newAutobuilderJavaRunner()
if _, err := compileJavaRunner.EnsureJava(); err != nil {
out.Fatalf("Failed to resolve Java for compilation: %s", err)
}
Expand All @@ -119,25 +115,6 @@ func init() {
compileCmd.Flags().StringVar(&CompileLogFile, "log-file", "", "Path to the log file (default: <cache-dir>/logs/<timestamp>.log)")
}

func ensureAutobuilderAvailable() (string, error) {
if globals.Config.Autobuilder.JarPath != "" {
return globals.Config.Autobuilder.JarPath, nil
}

autobuilderJarPath, err := utils.GetAutobuilderJarPath(globals.Config.Autobuilder.Version)
if err != nil {
return "", fmt.Errorf("failed to construct path to the autobuilder: %w", err)
}

if err = ensureArtifactAvailable("autobuilder", globals.Config.Autobuilder.Version, autobuilderJarPath, func() error {
return utils.DownloadGithubReleaseAsset(globals.Config.Owner, globals.Config.Repo, globals.Config.Autobuilder.Version, globals.AutobuilderAssetName, autobuilderJarPath, globals.Config.Github.Token, globals.Config.SkipVerify, out)
}); err != nil {
return "", err
}

return autobuilderJarPath, nil
}

func compile(absProjectRoot, absOutputProjectModelPath, autobuilderJarPath string, javaRunner java.JavaRunner) error {
if err := validation.ValidateCompileInputs(absProjectRoot, absOutputProjectModelPath); err != nil {
return err
Expand Down
Loading
Loading