zwischen 0.1.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: beb48773e7ec58217010758614752399220a826e232e7af0c4b46a0ae603e304
4
+ data.tar.gz: 4bd6a34f9299e1e623843476523e13bd736c5c2c21c19b6e37029d9a63af2810
5
+ SHA512:
6
+ metadata.gz: 72d2fda3d959f4b2621e8011fb03359e70b08db713665f30bff2129a98b2c09f19d2243a191d2a07739dec397570eb13a1c924e9a2f4d4ff71331c94ede9df0a
7
+ data.tar.gz: 624e96024130b6ab369f2d137d294941808d98c331a6ec1f2ecb1830cdbd5a30750be67a5840d179fd05f7c8ca3b2196c2643a2e32aff2baea025fae2d5e47a7
@@ -0,0 +1,49 @@
1
+ # Zwischen Configuration
2
+ # Copy this file to .zwischen.yml in your project root
3
+
4
+ # AI Provider Configuration
5
+ ai:
6
+ enabled: true # Enable AI for manual 'zwischen scan'
7
+ pre_push_enabled: false # Disable AI in pre-push hooks (performance)
8
+ provider: claude # Options: claude, ollama, openai
9
+
10
+ # Provider-specific settings
11
+ ollama:
12
+ model: llama3
13
+ url: http://localhost:11434
14
+
15
+ openai:
16
+ model: gpt-4
17
+
18
+ claude:
19
+ model: claude-3-5-sonnet-20241022
20
+
21
+ # api_key: null # Leave null to use ~/.zwischen/credentials or env vars (ANTHROPIC_API_KEY, OPENAI_API_KEY)
22
+
23
+ # What blocks a push
24
+ blocking:
25
+ severity: high # block on high or critical (default)
26
+ # severity: critical # only block on critical
27
+ # severity: none # never block, just warn
28
+
29
+ # Scanner Configuration
30
+ scanners:
31
+ gitleaks: true # Auto-installed to ~/.zwischen/bin/ if missing
32
+ semgrep: true # Optional, install with: pip install semgrep
33
+ # Or use detailed format:
34
+ # gitleaks:
35
+ # enabled: true
36
+ # semgrep:
37
+ # enabled: true
38
+ # config: p/security-audit # Open ruleset, no login required
39
+ # # Other options: p/secrets, p/owasp-top-ten, or path to custom ruleset
40
+
41
+ # Ignored Paths — findings in matching paths are dropped before reporting.
42
+
43
+ ignore:
44
+ - "**/vendor/**"
45
+ - "**/node_modules/**"
46
+ - "**/.git/**"
47
+ - "**/dist/**"
48
+ - "**/build/**"
49
+ - "**/test/fixtures/**"
data/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2026-06-10
11
+
12
+ ### Added
13
+ - Initial release
14
+ - Gitleaks and Semgrep scanner orchestration with auto-install
15
+ - AI-powered finding triage via Ollama, OpenAI, or Anthropic
16
+ - Pre-push git hook that blocks pushes introducing secrets or security issues
17
+ - Project type detection (Next.js, React, Django, Rails, and more)
18
+ - npm (`zwischen`) and pip (`zwischen-cli`) wrapper packages
19
+
20
+ [Unreleased]: https://github.com/cjordan223/zwischen/compare/v0.1.0...HEAD
21
+ [0.1.0]: https://github.com/cjordan223/zwischen/releases/tag/v0.1.0
data/DEVELOPMENT.md ADDED
@@ -0,0 +1,154 @@
1
+ # Development Guide
2
+
3
+ This repository contains three installable surfaces for the same product idea:
4
+
5
+ - `lib/zwischen`: the Ruby gem and canonical implementation.
6
+ - `packages/npm`: a Node.js wrapper package.
7
+ - `packages/pip`: a Python wrapper package.
8
+
9
+ When behavior differs, treat the Ruby gem as the source of truth unless the change is explicitly package-wrapper work.
10
+
11
+ ## Ruby Command Flow
12
+
13
+ ```text
14
+ bin/zwischen
15
+ -> Zwischen::CLI
16
+ -> Config.load
17
+ -> ProjectDetector.detect
18
+ -> Scanner::Orchestrator
19
+ -> Scanner::Gitleaks
20
+ -> Scanner::Semgrep
21
+ -> Finding::Aggregator
22
+ -> AI::Analyzer, when enabled
23
+ -> Reporter::Terminal
24
+ ```
25
+
26
+ `zwischen init` follows a separate path:
27
+
28
+ ```text
29
+ CLI#init
30
+ -> Setup.run
31
+ -> Installer checks/installs Gitleaks
32
+ -> Credentials saves ANTHROPIC_API_KEY when present
33
+ -> Hooks installs .git/hooks/pre-push
34
+ -> Config.init creates .zwischen.yml
35
+ ```
36
+
37
+ `zwischen scan --pre-push` uses `GitDiff.changed_files`, passes those files to scanner adapters, filters findings to changed files again as a safety net, and only prints compact output for blocking findings.
38
+
39
+ ## Config Contract
40
+
41
+ Project config lives at `.zwischen.yml`.
42
+
43
+ Current Ruby defaults:
44
+
45
+ - `ai.enabled: true`
46
+ - `ai.pre_push_enabled: false`
47
+ - `ai.provider: claude`
48
+ - `blocking.severity: high`
49
+ - Gitleaks and Semgrep enabled
50
+ - Semgrep config: `p/security-audit`
51
+
52
+ Credential lookup order is environment variables first, then `~/.zwischen/credentials`.
53
+
54
+ Supported Ruby credential environment variables:
55
+
56
+ - `ANTHROPIC_API_KEY` for `claude`
57
+ - `OPENAI_API_KEY` for `openai`
58
+
59
+ Local tool installs use `~/.zwischen/bin`.
60
+
61
+ ## Common Change Checklists
62
+
63
+ Scanner changes:
64
+
65
+ - Add or edit a scanner under `lib/zwischen/scanner`.
66
+ - Return `Zwischen::Finding::Finding` objects with normalized `type`, `scanner`, `severity`, `file`, `line`, `message`, and `rule_id`.
67
+ - Register new scanner selection in `Scanner::Orchestrator`.
68
+ - Update `.zwischen.yml.example`, `README.md`, and relevant specs.
69
+ - Add or update scanner specs.
70
+
71
+ AI provider changes:
72
+
73
+ - Add a client under `lib/zwischen/ai`.
74
+ - Wire provider selection in `AI::Analyzer`.
75
+ - Add credential mapping in `Credentials` if the provider needs an API key.
76
+ - Update `.zwischen.yml.example`, `README.md`, and AI specs.
77
+
78
+ Hook changes:
79
+
80
+ - Update `Hooks` and `Setup`.
81
+ - Run installed-gem tests from `TESTING.md`.
82
+ - Update `TESTING.md` with exact hook behavior and bypass behavior.
83
+
84
+ Package-wrapper parity changes:
85
+
86
+ - Mirror command flags in `packages/npm/bin/zwischen.js` and `packages/pip/zwischen/cli.py`.
87
+ - Keep config key names aligned with `.zwischen.yml.example`.
88
+ - Update wrapper package READMEs.
89
+
90
+ ## Known Iteration Points
91
+
92
+ - Ruby `Hooks.handle_existing_hook` has backup/append/skip logic, but `Setup#install_hook` currently backs up and replaces existing non-Zwischen hooks directly.
93
+ - Ruby config exposes `ignore` and `severity.fail_on`, but scanner adapters currently do not enforce ignore globs and blocking uses `blocking.severity`.
94
+ - npm and pip wrappers do not yet match Ruby feature parity. They do not support `uninstall`, `--only`, Ruby's changed-file pre-push filtering, or the Ruby JSON summary shape.
95
+ - npm and pip wrappers default AI provider to Ollama, while Ruby defaults to Claude.
96
+
97
+ ## Verification
98
+
99
+ Ruby unit suite:
100
+
101
+ ```bash
102
+ bundle exec rspec
103
+ ```
104
+
105
+ Installed-gem workflow:
106
+
107
+ ```bash
108
+ ./scripts/test_as_gem.sh
109
+ ```
110
+
111
+ Then follow `TESTING.md` from a temporary directory outside this repository.
112
+
113
+ npm wrapper smoke checks:
114
+
115
+ ```bash
116
+ cd packages/npm
117
+ npm test
118
+ node bin/zwischen.js --help
119
+ ```
120
+
121
+ pip wrapper smoke checks:
122
+
123
+ ```bash
124
+ cd packages/pip
125
+ python -m pip install -e .
126
+ zwischen --help
127
+ ```
128
+
129
+ ## Releasing
130
+
131
+ Releases are automated by `.github/workflows/release.yml`, triggered by a
132
+ version tag:
133
+
134
+ ```bash
135
+ # bump lib/zwischen/version.rb, packages/npm/package.json,
136
+ # packages/pip/pyproject.toml, and CHANGELOG.md first
137
+ git tag v0.1.0
138
+ git push origin v0.1.0
139
+ ```
140
+
141
+ One-time registry setup (first release only):
142
+
143
+ 1. **RubyGems** — add a *pending trusted publisher* for the `zwischen` gem at
144
+ <https://rubygems.org/profile/oidc/pending_trusted_publishers>:
145
+ repository `cjordan223/zwischen`, workflow `release.yml`, environment `release`.
146
+ 2. **PyPI** — add a *pending trusted publisher* for the `zwischen-cli` project at
147
+ <https://pypi.org/manage/account/publishing/> with the same repository,
148
+ workflow, and environment. (The bare `zwischen` name is taken on PyPI by an
149
+ unrelated package, so the distribution is `zwischen-cli`; the installed
150
+ command is still `zwischen`.)
151
+ 3. **npm** — create an automation token at npmjs.com and save it as the
152
+ `NPM_TOKEN` repository secret.
153
+ 4. **GitHub** — create a `release` environment in the repo settings
154
+ (Settings → Environments) so the OIDC claims match.
data/README.md ADDED
@@ -0,0 +1,207 @@
1
+ # Zwischen
2
+
3
+ [![CI](https://github.com/cjordan223/zwischen/actions/workflows/ci.yml/badge.svg)](https://github.com/cjordan223/zwischen/actions/workflows/ci.yml)
4
+ [![Gem Version](https://img.shields.io/gem/v/zwischen)](https://rubygems.org/gems/zwischen)
5
+ [![npm](https://img.shields.io/npm/v/zwischen)](https://www.npmjs.com/package/zwischen)
6
+ [![PyPI](https://img.shields.io/pypi/v/zwischen-cli)](https://pypi.org/project/zwischen-cli/)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
8
+
9
+ ![Zwischen blocking a push that contains an AWS secret](docs/demo.gif)
10
+
11
+ **Zwischen blocks `git push` the moment you're about to leak a secret** — the
12
+ last point where a leaked credential is still a local problem instead of an
13
+ incident. It orchestrates [Gitleaks](https://github.com/gitleaks/gitleaks)
14
+ and [Semgrep](https://semgrep.dev), normalizes their findings, and (optionally)
15
+ asks an AI provider — including fully local models via Ollama — to prioritize
16
+ the results, flag false positives, and suggest fixes.
17
+
18
+ One command sets everything up:
19
+
20
+ ```bash
21
+ zwischen init # installs gitleaks if missing, creates config, installs the pre-push hook
22
+ ```
23
+
24
+ ## Try it on a deliberately vulnerable app
25
+
26
+ The [zwischen-demo](https://github.com/cjordan223/zwischen-demo) repository
27
+ is a small Express app seeded with fake secrets and real vulnerability
28
+ patterns. Clone it and watch a push get blocked in under a minute.
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ gem install zwischen # canonical implementation (Ruby)
34
+ npm install -g zwischen # Node wrapper
35
+ pip install zwischen-cli # Python wrapper (command is still `zwischen`)
36
+ ```
37
+
38
+ For local development from this repository:
39
+
40
+ ```bash
41
+ bundle install
42
+ bundle exec ruby -Ilib bin/zwischen --help
43
+ ```
44
+
45
+ ## Quick start
46
+
47
+ Run these from the project you want to protect:
48
+
49
+ ```bash
50
+ zwischen init # config + pre-push hook + gitleaks auto-install
51
+ zwischen scan # manual scan with full report
52
+ zwischen doctor # check tool status
53
+ ```
54
+
55
+ ## AI triage
56
+
57
+ Raw scanner output tells you *what matched*. The AI pass tells you *what to
58
+ fix first and how* — see the real before/after in
59
+ [docs/triage-example.md](docs/triage-example.md), generated with a local
60
+ model via Ollama.
61
+
62
+ | Provider | Flag | Setup |
63
+ | --- | --- | --- |
64
+ | Claude | `--ai claude` | Set `ANTHROPIC_API_KEY` or pass `--api-key`. |
65
+ | Ollama | `--ai ollama` | Install Ollama and pull a model. Local-first: nothing leaves your machine. |
66
+ | OpenAI | `--ai openai` | Set `OPENAI_API_KEY` or pass `--api-key`. |
67
+
68
+ Manual scans use AI when `--ai` is passed or config enables it. Pre-push
69
+ scans stay scanner-only unless `ai.pre_push_enabled: true` — blocking
70
+ decisions should be fast and deterministic. The design rationale is in
71
+ [docs/design.md](docs/design.md).
72
+
73
+ ## Commands
74
+
75
+ | Command | What it does |
76
+ | --- | --- |
77
+ | `zwischen init` | Installs/checks tools, creates config, installs pre-push hook (backs up an existing non-Zwischen hook). |
78
+ | `zwischen scan` | Runs enabled scanners, prints a terminal report. |
79
+ | `zwischen scan --changed` | Scans only files changed since the default branch. |
80
+ | `zwischen scan --only secrets,sast` | Limits to Gitleaks (`secrets`) and/or Semgrep (`sast`). |
81
+ | `zwischen scan --ai <provider>` | Adds AI prioritization, fix suggestions, false-positive detection. |
82
+ | `zwischen scan --format json` | Machine-readable summary + findings. |
83
+ | `zwischen scan --format sarif` | SARIF 2.1.0 for GitHub code scanning. |
84
+ | `zwischen scan --pre-push` | Quiet hook mode: changed files only, compact output only when blocking. |
85
+ | `zwischen doctor` | Shows Gitleaks and Semgrep status. |
86
+ | `zwischen uninstall` | Removes the hook, optionally config/credentials. |
87
+
88
+ Escape hatches: `git push --no-verify` or `ZWISCHEN_SKIP=1 git push`.
89
+
90
+ ## GitHub Action
91
+
92
+ Run the same scan in CI and feed the GitHub Security tab:
93
+
94
+ ```yaml
95
+ - uses: cjordan223/zwischen@main
96
+ with:
97
+ sarif-file: zwischen.sarif
98
+ - uses: github/codeql-action/upload-sarif@v3
99
+ if: always()
100
+ with:
101
+ sarif_file: zwischen.sarif
102
+ ```
103
+
104
+ ## Configuration
105
+
106
+ Create or edit `.zwischen.yml` in the scanned project:
107
+
108
+ ```yaml
109
+ ai:
110
+ enabled: true
111
+ pre_push_enabled: false
112
+ provider: claude # claude, ollama, or openai
113
+ ollama:
114
+ model: llama3
115
+ url: http://localhost:11434
116
+ timeout: 180 # seconds; local models can be slow to load
117
+
118
+ blocking:
119
+ severity: high # high, critical, or none
120
+
121
+ scanners:
122
+ gitleaks:
123
+ enabled: true
124
+ semgrep:
125
+ enabled: true
126
+ config: p/security-audit,p/expressjs # comma-separated rulesets
127
+
128
+ ignore: # findings under these globs are dropped
129
+ - "**/node_modules/**"
130
+ - "**/test/fixtures/**"
131
+ ```
132
+
133
+ Boolean scanner entries (`gitleaks: true`) work too. Credentials are read
134
+ from environment variables first, then `~/.zwischen/credentials` (written by
135
+ `zwischen init` when `ANTHROPIC_API_KEY` is set).
136
+
137
+ ## Architecture
138
+
139
+ ```mermaid
140
+ flowchart LR
141
+ push[git push] --> hook[pre-push hook]
142
+ hook --> diff[GitDiff<br/>changed files]
143
+ diff --> orch[Orchestrator]
144
+ cli[zwischen scan] --> orch
145
+ orch --> gl[Gitleaks<br/>secrets]
146
+ orch --> sg[Semgrep<br/>SAST]
147
+ gl --> agg[Aggregator<br/>normalize + dedupe<br/>+ ignore globs]
148
+ sg --> agg
149
+ agg --> ai{AI enabled?}
150
+ ai -- yes --> llm[Claude / OpenAI / Ollama<br/>priority · fixes · false positives]
151
+ ai -- no --> rep
152
+ llm --> rep[Reporter]
153
+ rep --> term[terminal / compact]
154
+ rep --> mach[json / sarif]
155
+ term --> block{blocking<br/>finding?}
156
+ block -- yes --> deny[⛔ push blocked]
157
+ block -- no --> allow[✓ push proceeds]
158
+ ```
159
+
160
+ The Ruby gem is the canonical implementation; the npm and pip packages are
161
+ convenience wrappers with a smaller command surface.
162
+
163
+ ## Wrapper parity
164
+
165
+ | Capability | Ruby gem | npm / pip wrappers |
166
+ | --- | --- | --- |
167
+ | `init` / `scan` / `doctor` | ✓ | ✓ |
168
+ | `uninstall` | ✓ | — |
169
+ | `--only` scanner selection | ✓ | — |
170
+ | `--changed` / changed-file pre-push filtering | ✓ | — (hook scans the project) |
171
+ | `--format json` | ✓ | ✓ |
172
+ | `--format sarif` | ✓ | — |
173
+ | AI providers | claude, ollama, openai | ollama, openai, anthropic |
174
+
175
+ The wrapper gaps are intentional scope decisions, not bugs: the wrappers
176
+ exist so `npm install -g zwischen` / `pip install zwischen-cli` work in
177
+ ecosystems where a Ruby gem is friction, and they track the core workflow
178
+ (init → hook → scan) rather than every flag.
179
+
180
+ ## Repository layout
181
+
182
+ ```text
183
+ bin/zwischen Ruby executable
184
+ lib/zwischen/ Ruby gem implementation
185
+ lib/zwischen/scanner/ Gitleaks and Semgrep adapters
186
+ lib/zwischen/ai/ Claude, Ollama, and OpenAI clients
187
+ lib/zwischen/reporter/ Terminal and SARIF reporters
188
+ packages/npm/ Node wrapper package
189
+ packages/pip/ Python wrapper package
190
+ spec/ RSpec suite
191
+ action.yml Composite GitHub Action
192
+ docs/ Design write-up, triage example, demo GIF
193
+ ```
194
+
195
+ ## Development
196
+
197
+ ```bash
198
+ bundle exec rspec # 196+ examples
199
+ ./scripts/test_as_gem.sh # install and exercise as a real gem
200
+ ```
201
+
202
+ See [DEVELOPMENT.md](DEVELOPMENT.md) for architecture notes and the release
203
+ process, and [TESTING.md](TESTING.md) for the end-to-end test plan.
204
+
205
+ ## License
206
+
207
+ MIT