omamori 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/Gemfile +9 -0
- data/Gemfile.lock +97 -0
- data/LICENSE +21 -0
- data/README.md +172 -0
- data/README_ja.md +175 -0
- data/demo/ai_analysis_vulnerability.rb +28 -0
- data/demo/csrf_vulnerability.rb +31 -0
- data/demo/eval_vulnerability.rb +29 -0
- data/demo/idor_vulnerability.rb +39 -0
- data/demo/insecure_cookie_vulnerability.rb +25 -0
- data/demo/open_redirect_vulnerability.rb +22 -0
- data/demo/static_analysis_vulnerability.rb +18 -0
- data/demo/xss_vulnerability.rb +21 -0
- data/exe/omamori +16 -0
- data/lib/omamori/ai_analysis_engine/diff_splitter.rb +62 -0
- data/lib/omamori/ai_analysis_engine/gemini_client.rb +70 -0
- data/lib/omamori/ai_analysis_engine/prompt_manager.rb +76 -0
- data/lib/omamori/config.rb +144 -0
- data/lib/omamori/core_runner.rb +431 -0
- data/lib/omamori/report_generator/console_formatter.rb +132 -0
- data/lib/omamori/report_generator/html_formatter.rb +31 -0
- data/lib/omamori/report_generator/json_formatter.rb +19 -0
- data/lib/omamori/report_generator/report_template.erb +104 -0
- data/lib/omamori/static_analysers/brakeman_runner.rb +51 -0
- data/lib/omamori/static_analysers/bundler_audit_runner.rb +53 -0
- data/lib/omamori/version.rb +5 -0
- data/lib/omamori.rb +16 -0
- metadata +172 -0
@@ -0,0 +1,104 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Omamori Security Report</title>
|
5
|
+
<style>
|
6
|
+
body { font-family: sans-serif; line-height: 1.6; margin: 20px; }
|
7
|
+
h1 { color: #333; }
|
8
|
+
.risk { border: 1px solid #ccc; padding: 15px; margin-bottom: 15px; border-radius: 5px; }
|
9
|
+
.risk h2 { margin-top: 0; }
|
10
|
+
.severity-critical { color: red; font-weight: bold; }
|
11
|
+
.severity-high { color: red; font-weight: bold; }
|
12
|
+
.severity-medium { color: orange; }
|
13
|
+
.severity-low { color: blue; }
|
14
|
+
.severity-info { color: green; }
|
15
|
+
pre { background-color: #f4f4f4; padding: 10px; overflow-x: auto; }
|
16
|
+
</style>
|
17
|
+
</head>
|
18
|
+
<body>
|
19
|
+
<h1>Omamori Security Report</h1>
|
20
|
+
|
21
|
+
<h2>AI Analysis Results</h2>
|
22
|
+
<% if @ai_risks.empty? %>
|
23
|
+
<p>No AI-detected security risks.</p>
|
24
|
+
<% else %>
|
25
|
+
<% @ai_risks.each do |risk| %>
|
26
|
+
<div class="risk">
|
27
|
+
<h3 class="severity-<%= risk['severity'].downcase %>"><%= risk['type'] %></h3>
|
28
|
+
<p><strong>Severity:</strong> <span class="severity-<%= risk['severity'].downcase %>"><%= risk['severity'] %></span></p>
|
29
|
+
<p><strong>Location:</strong> <%= risk['location'] %></p>
|
30
|
+
<p><strong>Details:</strong> <%= risk['details'] %></p>
|
31
|
+
<p><strong>Code Snippet:</strong></p>
|
32
|
+
<pre><%= risk['code_snippet'] %></pre>
|
33
|
+
</div>
|
34
|
+
<% end %>
|
35
|
+
<% end %>
|
36
|
+
|
37
|
+
<h2>Static Analysis Results</h2>
|
38
|
+
<% if @static_results.empty? %>
|
39
|
+
<p>No static analysis results available.</p>
|
40
|
+
<% else %>
|
41
|
+
<h3>Brakeman</h3>
|
42
|
+
<% brakeman_result = @static_results['brakeman'] %>
|
43
|
+
<% if brakeman_result && brakeman_result['warnings'] && !brakeman_result['warnings'].empty? %>
|
44
|
+
<% brakeman_result['warnings'].each do |warning| %>
|
45
|
+
<div class="risk">
|
46
|
+
<h4 class="severity-<%= warning['confidence'].downcase %>">Brakeman Warning: <%= warning['warning_type'] %></h4>
|
47
|
+
<p><strong>Confidence:</strong> <span class="severity-<%= warning['confidence'].downcase %>"><%= warning['confidence'] %></span></p>
|
48
|
+
<p><strong>Message:</strong> <%= warning['message'] %></p>
|
49
|
+
<p><strong>File:</strong> <%= warning['file'] %></p>
|
50
|
+
<p><strong>Line:</strong> <%= warning['line'] %></p>
|
51
|
+
<p><strong>Code:</strong> <%= warning['code'] %></p>
|
52
|
+
<p><strong>Link:</strong> <a href="<%= warning['link'] %>" target="_blank"><%= warning['link'] %></a></p>
|
53
|
+
</div>
|
54
|
+
<% end %>
|
55
|
+
<% else %>
|
56
|
+
<p>No Brakeman warnings found.</p>
|
57
|
+
<% end %>
|
58
|
+
|
59
|
+
<h3>Bundler-Audit</h3>
|
60
|
+
<% bundler_audit_result = @static_results['bundler_audit'] %>
|
61
|
+
<% if bundler_audit_result && bundler_audit_result['scan'] %>
|
62
|
+
<% scan_results = bundler_audit_result['scan'] %>
|
63
|
+
<% if scan_results['vulnerabilities'] && !scan_results['vulnerabilities'].empty? %>
|
64
|
+
<h4>Vulnerabilities</h4>
|
65
|
+
<ul>
|
66
|
+
<% scan_results['vulnerabilities'].each do |vulnerability| %>
|
67
|
+
<li>
|
68
|
+
<strong>ID:</strong> <span class="severity-<%= vulnerability['criticality'].downcase %>"><%= vulnerability['id'] %></span><br>
|
69
|
+
<strong>Gem:</strong> <%= vulnerability['gem'] %><br>
|
70
|
+
<strong>Title:</strong> <%= vulnerability['title'] %><br>
|
71
|
+
<strong>URL:</strong> <a href="<%= vulnerability['url'] %>" target="_blank"><%= vulnerability['url'] %></a><br>
|
72
|
+
<strong>Criticality:</strong> <span class="severity-<%= vulnerability['criticality'].downcase %>"><%= vulnerability['criticality'] %></span><br>
|
73
|
+
<strong>Description:</strong> <%= vulnerability['description'] %><br>
|
74
|
+
<strong>Introduced In:</strong> <%= vulnerability['introduced_in'] %><br>
|
75
|
+
<strong>Patched Versions:</strong> <%= vulnerability['patched_versions'].join(', ') %><br>
|
76
|
+
<strong>Advisory Date:</strong> <%= vulnerability['advisory_date'] %>
|
77
|
+
</li>
|
78
|
+
<% end %>
|
79
|
+
</ul>
|
80
|
+
<% else %>
|
81
|
+
<p>No vulnerabilities found.</p>
|
82
|
+
<% end %>
|
83
|
+
|
84
|
+
<% if scan_results['unpatched_gems'] && !scan_results['unpatched_gems'].empty? %>
|
85
|
+
<h4>Unpatched Gems</h4>
|
86
|
+
<ul>
|
87
|
+
<% scan_results['unpatched_gems'].each do |gem| %>
|
88
|
+
<li>
|
89
|
+
<strong>Name:</strong> <%= gem['name'] %><br>
|
90
|
+
<strong>Version:</strong> <%= gem['version'] %>
|
91
|
+
</li>
|
92
|
+
<% end %>
|
93
|
+
</ul>
|
94
|
+
<% else %>
|
95
|
+
<p>No unpatched gems found.</p>
|
96
|
+
<% end %>
|
97
|
+
|
98
|
+
<% else %>
|
99
|
+
<p>Bundler-Audit results not available or in unexpected format.</p>
|
100
|
+
<% end %>
|
101
|
+
<% end %>
|
102
|
+
|
103
|
+
</body>
|
104
|
+
</html>
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Omamori
|
4
|
+
module StaticAnalysers
|
5
|
+
class BrakemanRunner
|
6
|
+
def initialize(options = {})
|
7
|
+
@options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
puts "Running Brakeman..."
|
12
|
+
# Determine Brakeman command based on options
|
13
|
+
# Use --force to run scan even if it's not a Rails application
|
14
|
+
# Use -f json for JSON output
|
15
|
+
# Include options passed during initialization
|
16
|
+
# Convert options hash to command line arguments string
|
17
|
+
options_string = @options.map do |key, value|
|
18
|
+
if value.is_a?(TrueClass)
|
19
|
+
key.to_s
|
20
|
+
elsif value.is_a?(FalseClass)
|
21
|
+
"" # Don't include false flags
|
22
|
+
else
|
23
|
+
"#{key} #{value}"
|
24
|
+
end
|
25
|
+
end.join(" ").strip
|
26
|
+
|
27
|
+
brakeman_command = "brakeman -f json . --force #{options_string}".strip # strip again in case options_string is empty
|
28
|
+
|
29
|
+
begin
|
30
|
+
# Execute the Brakeman command and capture output
|
31
|
+
brakeman_output = `#{brakeman_command}`
|
32
|
+
|
33
|
+
# Parse the JSON output
|
34
|
+
# Note: JSON.parse is called here. If the test expects it to be called only once,
|
35
|
+
# the test setup might be causing it to be called multiple times or the mock is misconfigured.
|
36
|
+
JSON.parse(brakeman_output)
|
37
|
+
rescue Errno::ENOENT
|
38
|
+
puts "Error: Brakeman command not found. Is Brakeman installed?"
|
39
|
+
nil
|
40
|
+
rescue JSON::ParserError
|
41
|
+
puts "Error: Failed to parse Brakeman JSON output."
|
42
|
+
puts "Raw output:\n#{brakeman_output}"
|
43
|
+
nil
|
44
|
+
rescue => e
|
45
|
+
puts "An error occurred during Brakeman execution: #{e.message}"
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json' # Bundler-Audit can output JSON
|
4
|
+
|
5
|
+
module Omamori
|
6
|
+
module StaticAnalysers
|
7
|
+
class BundlerAuditRunner
|
8
|
+
def initialize(options = {})
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
# TODO: Determine Bundler-Audit command based on options
|
14
|
+
# Example: bundle audit --format json
|
15
|
+
# Include options passed during initialization
|
16
|
+
# Build options string from the options hash
|
17
|
+
options_string = @options.map { |key, value| "--#{key.to_s.gsub('_', '-')}" + (value == true ? "" : " #{value}") }.join(" ")
|
18
|
+
bundler_audit_command = "bundle audit --format json#{options_string.empty? ? '' : " #{options_string}"}"
|
19
|
+
|
20
|
+
begin
|
21
|
+
# Execute the Bundler-Audit command and capture output
|
22
|
+
# Note: bundle audit exits with non-zero status if vulnerabilities are found
|
23
|
+
# We need to capture stdout and stderr regardless of exit status
|
24
|
+
bundler_audit_output = `#{bundler_audit_command} 2>&1`
|
25
|
+
|
26
|
+
# Parse the JSON output
|
27
|
+
# Bundler-Audit JSON output structure might vary, need to confirm
|
28
|
+
# Assuming it returns a JSON object with vulnerability information
|
29
|
+
parsed_output = JSON.parse(bundler_audit_output)
|
30
|
+
# Validate the parsed output structure
|
31
|
+
if parsed_output.is_a?(Hash) && parsed_output.key?('results')
|
32
|
+
# Return the entire parsed output hash
|
33
|
+
parsed_output
|
34
|
+
else
|
35
|
+
puts "Error: Unexpected Bundler-Audit JSON output structure."
|
36
|
+
puts "Raw output:\n#{bundler_audit_output}"
|
37
|
+
nil # Return nil if the structure is unexpected
|
38
|
+
end
|
39
|
+
rescue Errno::ENOENT
|
40
|
+
puts "Error: bundle command not found. Is Bundler installed?"
|
41
|
+
nil
|
42
|
+
rescue JSON::ParserError
|
43
|
+
puts "Error: Failed to parse Bundler-Audit JSON output."
|
44
|
+
puts "Raw output:\n#{bundler_audit_output}"
|
45
|
+
nil
|
46
|
+
rescue => e
|
47
|
+
puts "An error occurred during Bundler-Audit execution: #{e.message}"
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/omamori.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "omamori/version"
|
4
|
+
require_relative "omamori/core_runner"
|
5
|
+
require_relative "omamori/ai_analysis_engine/gemini_client"
|
6
|
+
require_relative "omamori/ai_analysis_engine/prompt_manager"
|
7
|
+
require_relative "omamori/ai_analysis_engine/diff_splitter"
|
8
|
+
require_relative "omamori/report_generator/console_formatter"
|
9
|
+
require_relative "omamori/report_generator/html_formatter"
|
10
|
+
require_relative "omamori/report_generator/json_formatter"
|
11
|
+
require_relative "omamori/static_analysers/brakeman_runner"
|
12
|
+
require_relative "omamori/static_analysers/bundler_audit_runner"
|
13
|
+
|
14
|
+
module Omamori
|
15
|
+
# Your code goes here...
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: omamori
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- rira100000000
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-04-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: colorize
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.8'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.8'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: dotenv
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '13.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '13.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: ruby-gemini-api
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.1.0
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.1.0
|
111
|
+
description: omamori scans Ruby code and diffs using AI (Google Gemini) to detect
|
112
|
+
security vulnerabilities often missed by traditional tools.
|
113
|
+
email:
|
114
|
+
- 101010hayakawa@gmail
|
115
|
+
executables:
|
116
|
+
- omamori
|
117
|
+
extensions: []
|
118
|
+
extra_rdoc_files: []
|
119
|
+
files:
|
120
|
+
- Gemfile
|
121
|
+
- Gemfile.lock
|
122
|
+
- LICENSE
|
123
|
+
- README.md
|
124
|
+
- README_ja.md
|
125
|
+
- demo/ai_analysis_vulnerability.rb
|
126
|
+
- demo/csrf_vulnerability.rb
|
127
|
+
- demo/eval_vulnerability.rb
|
128
|
+
- demo/idor_vulnerability.rb
|
129
|
+
- demo/insecure_cookie_vulnerability.rb
|
130
|
+
- demo/open_redirect_vulnerability.rb
|
131
|
+
- demo/static_analysis_vulnerability.rb
|
132
|
+
- demo/xss_vulnerability.rb
|
133
|
+
- exe/omamori
|
134
|
+
- lib/omamori.rb
|
135
|
+
- lib/omamori/ai_analysis_engine/diff_splitter.rb
|
136
|
+
- lib/omamori/ai_analysis_engine/gemini_client.rb
|
137
|
+
- lib/omamori/ai_analysis_engine/prompt_manager.rb
|
138
|
+
- lib/omamori/config.rb
|
139
|
+
- lib/omamori/core_runner.rb
|
140
|
+
- lib/omamori/report_generator/console_formatter.rb
|
141
|
+
- lib/omamori/report_generator/html_formatter.rb
|
142
|
+
- lib/omamori/report_generator/json_formatter.rb
|
143
|
+
- lib/omamori/report_generator/report_template.erb
|
144
|
+
- lib/omamori/static_analysers/brakeman_runner.rb
|
145
|
+
- lib/omamori/static_analysers/bundler_audit_runner.rb
|
146
|
+
- lib/omamori/version.rb
|
147
|
+
homepage: https://github.com/rira100000000/omamori
|
148
|
+
licenses: []
|
149
|
+
metadata:
|
150
|
+
homepage_uri: https://github.com/rira100000000/omamori
|
151
|
+
source_code_uri: https://github.com/rira100000000/omamori
|
152
|
+
changelog_uri: https://github.com/rira100000000/omamori/blob/main/CHANGELOG.md
|
153
|
+
post_install_message:
|
154
|
+
rdoc_options: []
|
155
|
+
require_paths:
|
156
|
+
- lib
|
157
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
158
|
+
requirements:
|
159
|
+
- - ">="
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
version: '0'
|
162
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
requirements: []
|
168
|
+
rubygems_version: 3.5.11
|
169
|
+
signing_key:
|
170
|
+
specification_version: 4
|
171
|
+
summary: AI-powered security vulnerability scanner for Ruby projects.
|
172
|
+
test_files: []
|