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 +7 -0
- data/.zwischen.yml.example +49 -0
- data/CHANGELOG.md +21 -0
- data/DEVELOPMENT.md +154 -0
- data/README.md +207 -0
- data/TESTING.md +374 -0
- data/bin/zwischen +7 -0
- data/lib/zwischen/ai/analyzer.rb +121 -0
- data/lib/zwischen/ai/anthropic_client.rb +59 -0
- data/lib/zwischen/ai/base_client.rb +27 -0
- data/lib/zwischen/ai/ollama_client.rb +59 -0
- data/lib/zwischen/ai/openai_client.rb +56 -0
- data/lib/zwischen/cli.rb +225 -0
- data/lib/zwischen/config.rb +159 -0
- data/lib/zwischen/credentials.rb +68 -0
- data/lib/zwischen/finding/aggregator.rb +78 -0
- data/lib/zwischen/finding/finding.rb +85 -0
- data/lib/zwischen/git_diff.rb +74 -0
- data/lib/zwischen/hooks.rb +93 -0
- data/lib/zwischen/installer.rb +215 -0
- data/lib/zwischen/project_detector.rb +217 -0
- data/lib/zwischen/reporter/sarif.rb +115 -0
- data/lib/zwischen/reporter/terminal.rb +190 -0
- data/lib/zwischen/scanner/base.rb +89 -0
- data/lib/zwischen/scanner/gitleaks.rb +115 -0
- data/lib/zwischen/scanner/orchestrator.rb +99 -0
- data/lib/zwischen/scanner/semgrep.rb +78 -0
- data/lib/zwischen/setup.rb +167 -0
- data/lib/zwischen/version.rb +5 -0
- data/lib/zwischen.rb +29 -0
- data/zwischen.gemspec +34 -0
- metadata +145 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "thor"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require_relative "credentials"
|
|
6
|
+
require_relative "hooks"
|
|
7
|
+
require_relative "config"
|
|
8
|
+
|
|
9
|
+
module Zwischen
|
|
10
|
+
class Setup
|
|
11
|
+
def self.run
|
|
12
|
+
new.run
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.uninstall
|
|
16
|
+
new.uninstall
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize
|
|
20
|
+
@shell = Thor::Shell::Color.new
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def run
|
|
24
|
+
@shell.say("\nš”ļø Installing Zwischen security layer...\n", :bold)
|
|
25
|
+
|
|
26
|
+
check_tools
|
|
27
|
+
configure_credentials
|
|
28
|
+
install_hook
|
|
29
|
+
create_config
|
|
30
|
+
|
|
31
|
+
@shell.say(" ā Done!", :green)
|
|
32
|
+
@shell.say("\nZwischen will now scan automatically before pushes.")
|
|
33
|
+
@shell.say("Run 'zwischen scan' to test it now.\n")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def uninstall
|
|
37
|
+
@shell.say("\nšļø Zwischen Uninstall\n", :bold)
|
|
38
|
+
|
|
39
|
+
project_root = Dir.pwd
|
|
40
|
+
hook_path = Hooks.hook_path(project_root)
|
|
41
|
+
|
|
42
|
+
# Remove hook
|
|
43
|
+
if Hooks.installed?(project_root)
|
|
44
|
+
if @shell.yes?("Remove git hook?", default: true)
|
|
45
|
+
if Hooks.uninstall(project_root)
|
|
46
|
+
@shell.say(" ā Removed .git/hooks/pre-push", :green)
|
|
47
|
+
else
|
|
48
|
+
@shell.say(" ā Failed to remove hook", :red)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
else
|
|
52
|
+
@shell.say(" ā³ No Zwischen hook found", :yellow)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Remove config
|
|
56
|
+
config_path = File.join(project_root, Config::CONFIG_FILE)
|
|
57
|
+
if File.exist?(config_path)
|
|
58
|
+
if @shell.yes?("Remove project config (.zwischen.yml)?", default: false)
|
|
59
|
+
File.delete(config_path)
|
|
60
|
+
@shell.say(" ā Removed .zwischen.yml", :green)
|
|
61
|
+
else
|
|
62
|
+
@shell.say(" ā³ Kept .zwischen.yml", :yellow)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Remove credentials
|
|
67
|
+
if File.exist?(Credentials.credentials_path)
|
|
68
|
+
if @shell.yes?("Remove global credentials (~/.zwischen/credentials)?", default: false)
|
|
69
|
+
File.delete(Credentials.credentials_path)
|
|
70
|
+
@shell.say(" ā Removed credentials", :green)
|
|
71
|
+
else
|
|
72
|
+
@shell.say(" ā³ Kept credentials", :yellow)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
@shell.say("\nā
Zwischen uninstalled from this project.\n", :green)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def check_tools
|
|
82
|
+
installer = Installer.new
|
|
83
|
+
|
|
84
|
+
# Auto-install gitleaks if missing
|
|
85
|
+
unless installer.gitleaks_available?
|
|
86
|
+
@shell.say(" ā³ Installing gitleaks...", :yellow)
|
|
87
|
+
if installer.auto_install_gitleaks
|
|
88
|
+
@shell.say(" ā Installed gitleaks to ~/.zwischen/bin/", :green)
|
|
89
|
+
else
|
|
90
|
+
@shell.say(" ā ļø Could not auto-install gitleaks", :yellow)
|
|
91
|
+
@shell.say(" ā #{installer.preferred_command('gitleaks')}", :yellow)
|
|
92
|
+
end
|
|
93
|
+
else
|
|
94
|
+
@shell.say(" ā gitleaks available", :green)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Check semgrep (optional, not auto-installed)
|
|
98
|
+
if installer.check_tool("semgrep")
|
|
99
|
+
@shell.say(" ā semgrep available", :green)
|
|
100
|
+
else
|
|
101
|
+
@shell.say(" ā³ semgrep not found (optional)", :yellow)
|
|
102
|
+
@shell.say(" ā #{installer.preferred_command('semgrep')}", :yellow)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def configure_credentials
|
|
107
|
+
api_key = ENV["ANTHROPIC_API_KEY"]
|
|
108
|
+
return unless api_key && !api_key.strip.empty?
|
|
109
|
+
|
|
110
|
+
Credentials.save(api_key: api_key)
|
|
111
|
+
@shell.say(" ā Credentials stored in ~/.zwischen/credentials - never committed", :green)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def install_hook
|
|
115
|
+
project_root = Dir.pwd
|
|
116
|
+
git_dir = File.join(project_root, ".git")
|
|
117
|
+
|
|
118
|
+
unless File.directory?(git_dir)
|
|
119
|
+
@shell.say(" ā ļø No .git directory found. Skipping hook installation.", :yellow)
|
|
120
|
+
return false
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
hook_path = Hooks.hook_path(project_root)
|
|
124
|
+
|
|
125
|
+
if File.exist?(hook_path)
|
|
126
|
+
if Hooks.zwischen_hook?(hook_path)
|
|
127
|
+
@shell.say(" ā Pre-push hook already installed", :green)
|
|
128
|
+
return true
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
backup_path = "#{hook_path}.zwischen.backup"
|
|
132
|
+
if File.exist?(backup_path)
|
|
133
|
+
timestamp = Time.now.strftime("%Y%m%d%H%M%S")
|
|
134
|
+
backup_path = "#{backup_path}.#{timestamp}"
|
|
135
|
+
end
|
|
136
|
+
FileUtils.cp(hook_path, backup_path)
|
|
137
|
+
@shell.say(" ā Backed up existing hook to #{backup_path}", :green)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
if Hooks.install(project_root)
|
|
141
|
+
@shell.say(" ā Installing pre-push hook", :green)
|
|
142
|
+
true
|
|
143
|
+
else
|
|
144
|
+
@shell.say(" ā Failed to install hook", :red)
|
|
145
|
+
false
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def create_config
|
|
150
|
+
project_root = Dir.pwd
|
|
151
|
+
config_path = File.join(project_root, Config::CONFIG_FILE)
|
|
152
|
+
|
|
153
|
+
if File.exist?(config_path)
|
|
154
|
+
@shell.say(" ā Config already exists (.zwischen.yml)", :green)
|
|
155
|
+
return false
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
if Config.init(project_root, quiet: true)
|
|
159
|
+
@shell.say(" ā Creating config (.zwischen.yml)", :green)
|
|
160
|
+
true
|
|
161
|
+
else
|
|
162
|
+
@shell.say(" ā Failed to create config", :red)
|
|
163
|
+
false
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
data/lib/zwischen.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "zwischen/version"
|
|
4
|
+
|
|
5
|
+
module Zwischen
|
|
6
|
+
class Error < StandardError; end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Load all modules when gem is required
|
|
10
|
+
require_relative "zwischen/config"
|
|
11
|
+
require_relative "zwischen/project_detector"
|
|
12
|
+
require_relative "zwischen/finding/finding"
|
|
13
|
+
require_relative "zwischen/finding/aggregator"
|
|
14
|
+
require_relative "zwischen/scanner/base"
|
|
15
|
+
require_relative "zwischen/scanner/gitleaks"
|
|
16
|
+
require_relative "zwischen/scanner/semgrep"
|
|
17
|
+
require_relative "zwischen/scanner/orchestrator"
|
|
18
|
+
require_relative "zwischen/installer"
|
|
19
|
+
require_relative "zwischen/credentials"
|
|
20
|
+
require_relative "zwischen/git_diff"
|
|
21
|
+
require_relative "zwischen/hooks"
|
|
22
|
+
require_relative "zwischen/setup"
|
|
23
|
+
require_relative "zwischen/ai/anthropic_client"
|
|
24
|
+
require_relative "zwischen/ai/ollama_client"
|
|
25
|
+
require_relative "zwischen/ai/openai_client"
|
|
26
|
+
require_relative "zwischen/ai/analyzer"
|
|
27
|
+
require_relative "zwischen/reporter/terminal"
|
|
28
|
+
require_relative "zwischen/reporter/sarif"
|
|
29
|
+
require_relative "zwischen/cli"
|
data/zwischen.gemspec
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/zwischen/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "zwischen"
|
|
7
|
+
spec.version = Zwischen::VERSION
|
|
8
|
+
spec.authors = ["Conner Jordan"]
|
|
9
|
+
spec.email = ["connercharlesjordan@gmail.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "AI-augmented security scanning CLI for vibe coders"
|
|
12
|
+
spec.description = "Orchestrates Gitleaks and Semgrep scanners, aggregates findings, and uses AI to prioritize and explain security issues"
|
|
13
|
+
spec.homepage = "https://github.com/cjordan223/zwischen"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
spec.required_ruby_version = ">= 3.3.0"
|
|
16
|
+
|
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
18
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
19
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
20
|
+
|
|
21
|
+
spec.files = Dir["lib/**/*", "bin/**/*", "*.md", "*.gemspec", ".zwischen.yml.example"].reject do |f|
|
|
22
|
+
File.directory?(f) || f.start_with?(*%w[spec/ test/ features/ .git .github])
|
|
23
|
+
end
|
|
24
|
+
spec.bindir = "bin"
|
|
25
|
+
spec.executables = ["zwischen"]
|
|
26
|
+
spec.require_paths = ["lib"]
|
|
27
|
+
|
|
28
|
+
spec.add_dependency "thor", "~> 1.3"
|
|
29
|
+
spec.add_dependency "faraday", "~> 2.0"
|
|
30
|
+
spec.add_dependency "colorize", "~> 0.8.1"
|
|
31
|
+
|
|
32
|
+
spec.add_development_dependency "rspec", "~> 3.12"
|
|
33
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
|
34
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: zwischen
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Conner Jordan
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: thor
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.3'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.3'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: faraday
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '2.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '2.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: colorize
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 0.8.1
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: 0.8.1
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: rspec
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '3.12'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '3.12'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: rake
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '13.0'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '13.0'
|
|
82
|
+
description: Orchestrates Gitleaks and Semgrep scanners, aggregates findings, and
|
|
83
|
+
uses AI to prioritize and explain security issues
|
|
84
|
+
email:
|
|
85
|
+
- connercharlesjordan@gmail.com
|
|
86
|
+
executables:
|
|
87
|
+
- zwischen
|
|
88
|
+
extensions: []
|
|
89
|
+
extra_rdoc_files: []
|
|
90
|
+
files:
|
|
91
|
+
- ".zwischen.yml.example"
|
|
92
|
+
- CHANGELOG.md
|
|
93
|
+
- DEVELOPMENT.md
|
|
94
|
+
- README.md
|
|
95
|
+
- TESTING.md
|
|
96
|
+
- bin/zwischen
|
|
97
|
+
- lib/zwischen.rb
|
|
98
|
+
- lib/zwischen/ai/analyzer.rb
|
|
99
|
+
- lib/zwischen/ai/anthropic_client.rb
|
|
100
|
+
- lib/zwischen/ai/base_client.rb
|
|
101
|
+
- lib/zwischen/ai/ollama_client.rb
|
|
102
|
+
- lib/zwischen/ai/openai_client.rb
|
|
103
|
+
- lib/zwischen/cli.rb
|
|
104
|
+
- lib/zwischen/config.rb
|
|
105
|
+
- lib/zwischen/credentials.rb
|
|
106
|
+
- lib/zwischen/finding/aggregator.rb
|
|
107
|
+
- lib/zwischen/finding/finding.rb
|
|
108
|
+
- lib/zwischen/git_diff.rb
|
|
109
|
+
- lib/zwischen/hooks.rb
|
|
110
|
+
- lib/zwischen/installer.rb
|
|
111
|
+
- lib/zwischen/project_detector.rb
|
|
112
|
+
- lib/zwischen/reporter/sarif.rb
|
|
113
|
+
- lib/zwischen/reporter/terminal.rb
|
|
114
|
+
- lib/zwischen/scanner/base.rb
|
|
115
|
+
- lib/zwischen/scanner/gitleaks.rb
|
|
116
|
+
- lib/zwischen/scanner/orchestrator.rb
|
|
117
|
+
- lib/zwischen/scanner/semgrep.rb
|
|
118
|
+
- lib/zwischen/setup.rb
|
|
119
|
+
- lib/zwischen/version.rb
|
|
120
|
+
- zwischen.gemspec
|
|
121
|
+
homepage: https://github.com/cjordan223/zwischen
|
|
122
|
+
licenses:
|
|
123
|
+
- MIT
|
|
124
|
+
metadata:
|
|
125
|
+
homepage_uri: https://github.com/cjordan223/zwischen
|
|
126
|
+
source_code_uri: https://github.com/cjordan223/zwischen
|
|
127
|
+
changelog_uri: https://github.com/cjordan223/zwischen/blob/main/CHANGELOG.md
|
|
128
|
+
rdoc_options: []
|
|
129
|
+
require_paths:
|
|
130
|
+
- lib
|
|
131
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
132
|
+
requirements:
|
|
133
|
+
- - ">="
|
|
134
|
+
- !ruby/object:Gem::Version
|
|
135
|
+
version: 3.3.0
|
|
136
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
137
|
+
requirements:
|
|
138
|
+
- - ">="
|
|
139
|
+
- !ruby/object:Gem::Version
|
|
140
|
+
version: '0'
|
|
141
|
+
requirements: []
|
|
142
|
+
rubygems_version: 3.6.9
|
|
143
|
+
specification_version: 4
|
|
144
|
+
summary: AI-augmented security scanning CLI for vibe coders
|
|
145
|
+
test_files: []
|