trapline 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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +585 -0
  4. data/config.ru.example +21 -0
  5. data/exe/trapline +8 -0
  6. data/lib/generators/trapline/install_generator.rb +34 -0
  7. data/lib/generators/trapline/templates/initializer.rb +23 -0
  8. data/lib/tasks/trapline.rake +19 -0
  9. data/lib/trapline/cli.rb +212 -0
  10. data/lib/trapline/configuration.rb +19 -0
  11. data/lib/trapline/cve/client.rb +203 -0
  12. data/lib/trapline/cve/record.rb +160 -0
  13. data/lib/trapline/engine.rb +243 -0
  14. data/lib/trapline/generator.rb +236 -0
  15. data/lib/trapline/languages.rb +65 -0
  16. data/lib/trapline/railtie.rb +26 -0
  17. data/lib/trapline/rule.rb +165 -0
  18. data/lib/trapline/runtime.rb +148 -0
  19. data/lib/trapline/store.rb +113 -0
  20. data/lib/trapline/suite.rb +44 -0
  21. data/lib/trapline/templates/cwe/cwe-22.yml +167 -0
  22. data/lib/trapline/templates/cwe/cwe-327.yml +124 -0
  23. data/lib/trapline/templates/cwe/cwe-502.yml +123 -0
  24. data/lib/trapline/templates/cwe/cwe-601.yml +124 -0
  25. data/lib/trapline/templates/cwe/cwe-611.yml +87 -0
  26. data/lib/trapline/templates/cwe/cwe-78.yml +166 -0
  27. data/lib/trapline/templates/cwe/cwe-79.yml +164 -0
  28. data/lib/trapline/templates/cwe/cwe-798.yml +125 -0
  29. data/lib/trapline/templates/cwe/cwe-89.yml +189 -0
  30. data/lib/trapline/templates/cwe/cwe-917.yml +46 -0
  31. data/lib/trapline/templates/cwe/cwe-918.yml +166 -0
  32. data/lib/trapline/templates/cwe/cwe-94.yml +131 -0
  33. data/lib/trapline/templates.rb +261 -0
  34. data/lib/trapline/validator.rb +359 -0
  35. data/lib/trapline/version.rb +5 -0
  36. data/lib/trapline/web/app.rb +234 -0
  37. data/lib/trapline/web/assets/THIRD_PARTY_LICENSES.txt +214 -0
  38. data/lib/trapline/web/assets/app.css +399 -0
  39. data/lib/trapline/web/assets/app.js +804 -0
  40. data/lib/trapline/web/assets/codemirror.css +344 -0
  41. data/lib/trapline/web/assets/codemirror.js +13263 -0
  42. data/lib/trapline/web/assets/index.html +165 -0
  43. data/lib/trapline/web/assets/jetbrains-mono-latin-400-normal.woff2 +0 -0
  44. data/lib/trapline/web/assets/jetbrains-mono-latin-600-normal.woff2 +0 -0
  45. data/lib/trapline/web/assets/public-sans-latin-400-normal.woff2 +0 -0
  46. data/lib/trapline/web/assets/public-sans-latin-500-normal.woff2 +0 -0
  47. data/lib/trapline/web/assets/public-sans-latin-600-normal.woff2 +0 -0
  48. data/lib/trapline/web/assets/public-sans-latin-700-normal.woff2 +0 -0
  49. data/lib/trapline/web/server.rb +108 -0
  50. data/lib/trapline.rb +70 -0
  51. metadata +136 -0
@@ -0,0 +1,165 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <base href="__BASE__">
7
+ <title>Trapline</title>
8
+ <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22%3E%3Crect x=%2211%22 y=%2213%22 width=%229%22 height=%224%22 rx=%221%22 fill=%22%23f2b33d%22/%3E%3Cpath d=%22M4 5h9M4 10h16M4 15h6M4 20h12%22 stroke=%22%231c2733%22 stroke-width=%222%22 stroke-linecap=%22round%22/%3E%3C/svg%3E">
9
+ <link rel="stylesheet" href="assets/codemirror.css">
10
+ <link rel="stylesheet" href="assets/app.css">
11
+ </head>
12
+ <body>
13
+ <header class="top">
14
+ <div class="brand">
15
+ <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 5h9M4 10h16M4 15h6M4 20h12" /><rect x="11" y="13" width="9" height="4" rx="1" /></svg>
16
+ <span>Trapline</span>
17
+ </div>
18
+ <div class="doc">
19
+ <span id="doc-name" class="doc-name">Untitled rule</span>
20
+ <span id="doc-state" class="doc-state" aria-live="polite"></span>
21
+ </div>
22
+ <div class="top-actions">
23
+ <span id="engine" class="engine" title="Engine used to run the rules"></span>
24
+ <button id="btn-download" class="ghost" type="button" title="Download the YAML">Download</button>
25
+ <button id="btn-delete" class="ghost danger" type="button" hidden>Delete</button>
26
+ <button id="btn-save" class="primary" type="button" title="Ctrl+S">Save</button>
27
+ </div>
28
+ </header>
29
+
30
+ <div class="layout">
31
+ <aside class="side" aria-label="Rules">
32
+ <div class="side-actions">
33
+ <button id="btn-new" type="button">New rule</button>
34
+ <button id="btn-cve" type="button" class="accent">Generate from CVE</button>
35
+ </div>
36
+ <label class="search">
37
+ <span class="sr-only">Filter rules</span>
38
+ <input id="filter" type="search" placeholder="Filter by id, CVE or CWE">
39
+ </label>
40
+ <ul id="rule-list" class="rule-list"></ul>
41
+ <p id="rules-path" class="rules-path"></p>
42
+ </aside>
43
+
44
+ <main class="work">
45
+ <section class="pane pane-rule" aria-label="Rule">
46
+ <details id="details" class="details">
47
+ <summary>
48
+ <span>Rule details</span>
49
+ <span id="details-summary" class="details-summary"></span>
50
+ </summary>
51
+ <form id="fields" class="fields" autocomplete="off">
52
+ <label class="f-id">Id <input name="id" required spellcheck="false"></label>
53
+ <label class="f-sev">Severity <select name="severity"></select></label>
54
+ <label class="f-lang">Languages <input name="languages" placeholder="ruby, python" spellcheck="false"></label>
55
+ <label class="f-msg">Message <textarea name="message" rows="2"></textarea></label>
56
+ <label class="f-cwe">CWE <input name="cwe" placeholder="CWE-89: SQL Injection"></label>
57
+ <label class="f-cve">CVE <input name="cve" placeholder="CVE-2024-1234" spellcheck="false"></label>
58
+ <label class="f-conf">Confidence
59
+ <select name="confidence"><option value="">—</option><option>LOW</option><option>MEDIUM</option><option>HIGH</option></select>
60
+ </label>
61
+ <label class="f-refs">References <textarea name="references" rows="2" placeholder="One URL per line" spellcheck="false"></textarea></label>
62
+ </form>
63
+ </details>
64
+ <div class="pane-head">
65
+ <h2>Rule <span class="hint">YAML</span></h2>
66
+ <span id="mode" class="mode"></span>
67
+ </div>
68
+ <div id="yaml-editor" class="editor"></div>
69
+ <div id="issues" class="issues" aria-live="polite"></div>
70
+ </section>
71
+
72
+ <section class="pane pane-test" aria-label="Test">
73
+ <div class="pane-head">
74
+ <h2>Test code <span class="hint" title="Click the margin next to a line: must match → must not match → no expectation">click the margin to mark</span></h2>
75
+ <div class="test-controls">
76
+ <label class="sr-only" for="test-lang">Test language</label>
77
+ <select id="test-lang"></select>
78
+ <label class="auto" title="Show the ruleid/ok comments instead of badges"><input id="raw-ann" type="checkbox"> Show comments</label>
79
+ <label class="auto"><input id="auto-run" type="checkbox" checked> Run on edit</label>
80
+ <button id="btn-run" class="primary" type="button" title="Ctrl+Enter">Run</button>
81
+ </div>
82
+ </div>
83
+ <div id="run-status" class="run-status" aria-live="polite"></div>
84
+ <div id="test-editor" class="editor"></div>
85
+ <div id="results" class="results" aria-live="polite">
86
+ <p class="empty">Write a vulnerable example and a fixed one, annotated with <code>ruleid:</code> and <code>ok:</code>, and run the rule.</p>
87
+ </div>
88
+ </section>
89
+ </main>
90
+ </div>
91
+
92
+ <dialog id="dlg-new" class="dlg">
93
+ <form method="dialog" id="form-new">
94
+ <h3>New rule</h3>
95
+ <fieldset class="kinds" id="kinds"></fieldset>
96
+ <div class="row">
97
+ <label>Id <input name="id" value="my-rule" spellcheck="false" required></label>
98
+ <label>Language <select name="language" id="new-lang"></select></label>
99
+ </div>
100
+ <div class="dlg-actions">
101
+ <button value="cancel" type="submit" class="ghost" formnovalidate>Cancel</button>
102
+ <button value="ok" type="submit" class="primary">Create</button>
103
+ </div>
104
+ </form>
105
+ </dialog>
106
+
107
+ <dialog id="dlg-cve" class="dlg dlg-wide">
108
+ <form id="form-cve">
109
+ <h3>Generate a rule from a CVE</h3>
110
+ <p class="dlg-lead">Looks up the vulnerability in NVD and OSV and builds drafts from the CWE and the affected packages.</p>
111
+ <div class="row">
112
+ <label class="grow">CVE or GHSA <input name="id" placeholder="CVE-2021-44228" spellcheck="false" required></label>
113
+ <label>Language <select name="language" id="cve-lang"><option value="">Detect</option></select></label>
114
+ <button type="submit" class="primary" id="btn-cve-fetch">Search</button>
115
+ </div>
116
+ <details class="manual" id="cve-manual">
117
+ <summary>No network access? Enter the data manually</summary>
118
+ <div class="manual-grid">
119
+ <label>CWE <input name="m_cwe" placeholder="CWE-89"></label>
120
+ <label>Ecosystem
121
+ <select name="m_ecosystem"><option value="">—</option><option>RubyGems</option><option>PyPI</option><option>npm</option><option>Maven</option><option>Go</option></select>
122
+ </label>
123
+ <label>Package <input name="m_package" placeholder="package name" spellcheck="false"></label>
124
+ <label>Fixed in <input name="m_fixed" placeholder="1.2.3" spellcheck="false"></label>
125
+ <label class="span">Description <textarea name="m_description" rows="3"></textarea></label>
126
+ <button type="button" id="btn-cve-manual">Generate with this data</button>
127
+ </div>
128
+ </details>
129
+ </form>
130
+ <div id="cve-result" class="cve-result"></div>
131
+ <div class="dlg-actions">
132
+ <button type="button" class="ghost" id="btn-cve-close">Close</button>
133
+ <button type="button" class="primary" id="btn-cve-open" disabled>Open in editor</button>
134
+ </div>
135
+ </dialog>
136
+
137
+ <dialog id="dlg-save" class="dlg">
138
+ <form method="dialog" id="form-save">
139
+ <h3>Save rule</h3>
140
+ <label>File name <input name="slug" required spellcheck="false" pattern="[A-Za-z0-9][A-Za-z0-9._\-\/]*"></label>
141
+ <p class="dlg-lead" id="save-path"></p>
142
+ <div class="dlg-actions">
143
+ <button value="cancel" type="submit" class="ghost" formnovalidate>Cancel</button>
144
+ <button value="ok" type="submit" class="primary">Save</button>
145
+ </div>
146
+ </form>
147
+ </dialog>
148
+
149
+ <dialog id="dlg-confirm" class="dlg">
150
+ <form method="dialog">
151
+ <h3 id="confirm-title"></h3>
152
+ <p class="dlg-lead" id="confirm-text"></p>
153
+ <div class="dlg-actions">
154
+ <button value="cancel" class="ghost">Cancel</button>
155
+ <button value="ok" class="primary" id="confirm-ok"></button>
156
+ </div>
157
+ </form>
158
+ </dialog>
159
+
160
+ <div id="toast" class="toast" role="status" hidden></div>
161
+
162
+ <script src="assets/codemirror.js"></script>
163
+ <script src="assets/app.js"></script>
164
+ </body>
165
+ </html>
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "socket"
4
+ require "stringio"
5
+ require "uri"
6
+
7
+ module Trapline
8
+ module Web
9
+ module Server
10
+ module_function
11
+
12
+ def start(app, host: "127.0.0.1", port: 9292, logger: $stdout)
13
+ handler = rack_handler
14
+ url = "http://#{host}:#{port}/"
15
+ if handler
16
+ logger.puts "Trapline #{VERSION} at #{url} (#{handler.name})"
17
+ handler.run(app, Host: host, Port: port)
18
+ else
19
+ logger.puts "Trapline #{VERSION} at #{url} (built-in server)"
20
+ Builtin.new(app, host: host, port: port, logger: logger).run
21
+ end
22
+ end
23
+
24
+ def rack_handler
25
+ return nil if ENV["TRAPLINE_BUILTIN_SERVER"] == "1"
26
+
27
+ require "rackup"
28
+ %w[puma webrick].each do |name|
29
+ return Rackup::Handler.get(name)
30
+ rescue LoadError, NameError
31
+ next
32
+ end
33
+ nil
34
+ rescue LoadError
35
+ nil
36
+ end
37
+
38
+ class Builtin
39
+ STATUS = { 200 => "OK", 201 => "Created", 204 => "No Content", 400 => "Bad Request", 401 => "Unauthorized",
40
+ 403 => "Forbidden", 404 => "Not Found", 413 => "Payload Too Large", 422 => "Unprocessable Entity",
41
+ 500 => "Internal Server Error", 502 => "Bad Gateway", 503 => "Service Unavailable" }.freeze
42
+
43
+ def initialize(app, host:, port:, logger:)
44
+ @app = app
45
+ @host = host
46
+ @port = port
47
+ @logger = logger
48
+ end
49
+
50
+ # Closing the socket from a trap handler while #accept blocks raises
51
+ # ThreadError, so INT/TERM only interrupt the loop.
52
+ def run
53
+ server = TCPServer.new(@host, @port)
54
+ previous = trap("TERM") { Thread.main.raise(Interrupt) }
55
+ loop do
56
+ client = server.accept
57
+ Thread.new(client) { |c| handle(c) }
58
+ end
59
+ rescue Interrupt, IOError
60
+ @logger.puts "Stopping Trapline"
61
+ ensure
62
+ server&.close rescue nil
63
+ trap("TERM", previous || "DEFAULT")
64
+ end
65
+
66
+ def handle(client)
67
+ request_line = client.gets("\r\n", 8192).to_s
68
+ method, target, = request_line.split(" ", 3)
69
+ return client.close if method.nil? || target.nil?
70
+
71
+ headers = {}
72
+ while (line = client.gets("\r\n", 8192)) && line != "\r\n"
73
+ k, v = line.split(":", 2)
74
+ headers[k.strip.downcase] = v.to_s.strip
75
+ end
76
+ length = headers["content-length"].to_i
77
+ body = length.positive? ? client.read([length, App::MAX_BODY + 1].min).to_s : ""
78
+
79
+ uri = URI.parse(target)
80
+ env = {
81
+ "REQUEST_METHOD" => method, "SCRIPT_NAME" => "", "PATH_INFO" => uri.path,
82
+ "QUERY_STRING" => uri.query.to_s, "SERVER_NAME" => @host, "SERVER_PORT" => @port.to_s,
83
+ "SERVER_PROTOCOL" => "HTTP/1.1", "rack.input" => StringIO.new(body), "rack.errors" => $stderr,
84
+ "rack.url_scheme" => "http", "CONTENT_TYPE" => headers["content-type"].to_s,
85
+ "CONTENT_LENGTH" => length.to_s
86
+ }
87
+ headers.each { |k, v| env["HTTP_#{k.upcase.tr('-', '_')}"] = v unless %w[content-type content-length].include?(k) }
88
+
89
+ status, res_headers, res_body = @app.call(env)
90
+ payload = String.new(encoding: Encoding::BINARY)
91
+ res_body.each { |chunk| payload << chunk.b }
92
+ res_body.close if res_body.respond_to?(:close)
93
+
94
+ client.write "HTTP/1.1 #{status} #{STATUS.fetch(status, 'OK')}\r\n"
95
+ res_headers.each { |k, v| client.write "#{k}: #{v}\r\n" }
96
+ client.write "content-length: #{payload.bytesize}\r\nconnection: close\r\n\r\n"
97
+ client.write payload
98
+ @logger.puts "#{method} #{uri.path} #{status}" if ENV["TRAPLINE_LOG"]
99
+ rescue StandardError => e
100
+ warn "trapline: #{e.class}: #{e.message}"
101
+ client.write "HTTP/1.1 500 Internal Server Error\r\ncontent-length: 0\r\nconnection: close\r\n\r\n" rescue nil
102
+ ensure
103
+ client.close rescue nil
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
data/lib/trapline.rb ADDED
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+ require_relative "trapline/version"
5
+ require_relative "trapline/configuration"
6
+ require_relative "trapline/languages"
7
+ require_relative "trapline/rule"
8
+ require_relative "trapline/validator"
9
+ require_relative "trapline/engine"
10
+ require_relative "trapline/store"
11
+ require_relative "trapline/cve/record"
12
+ require_relative "trapline/cve/client"
13
+ require_relative "trapline/templates"
14
+ require_relative "trapline/runtime"
15
+ require_relative "trapline/generator"
16
+ require_relative "trapline/suite"
17
+
18
+ module Trapline
19
+ class Error < StandardError; end
20
+
21
+ class ParseError < Error
22
+ attr_reader :line, :column
23
+
24
+ def initialize(message, line: nil, column: nil)
25
+ super(message)
26
+ @line = line
27
+ @column = column
28
+ end
29
+ end
30
+
31
+ class NotFound < Error; end
32
+ class InvalidSlug < Error; end
33
+ class NetworkError < Error; end
34
+
35
+ class << self
36
+ def config
37
+ @config ||= Configuration.new
38
+ end
39
+
40
+ def configure
41
+ yield config
42
+ end
43
+
44
+ def reset_config!
45
+ @config = Configuration.new
46
+ @engine = nil
47
+ end
48
+
49
+ attr_writer :engine
50
+
51
+ def engine
52
+ @engine ||= Engine.detect(config.engine_binary)
53
+ end
54
+
55
+ def store
56
+ Store.new(config.rules_path)
57
+ end
58
+
59
+ def validate(yaml, engine: false)
60
+ Validator.new(engine: engine ? self.engine : nil).validate(yaml)
61
+ end
62
+
63
+ def generate_from_cve(cve_id, **opts)
64
+ Generator.new(**opts).from_cve(cve_id)
65
+ end
66
+ end
67
+ end
68
+
69
+ require_relative "trapline/web/app"
70
+ require_relative "trapline/railtie" if defined?(Rails::Railtie)
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: trapline
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jose Augusto
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2026-11-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '7'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '5.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '7'
33
+ - !ruby/object:Gem::Dependency
34
+ name: rake
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '13.0'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '13.0'
47
+ description: |
48
+ Trapline is a web editor for creating, testing and maintaining semgrep/opengrep
49
+ rules, with structural validation, execution with ruleid/ok annotations and a
50
+ draft generator from CVEs (NVD + OSV) with per-CWE templates. It works as a
51
+ CLI (`trapline server`), as a Rack app and mounted in a Rails app.
52
+ email:
53
+ - joseaugusto.881@outlook.com
54
+ executables:
55
+ - trapline
56
+ extensions: []
57
+ extra_rdoc_files: []
58
+ files:
59
+ - LICENSE
60
+ - README.md
61
+ - config.ru.example
62
+ - exe/trapline
63
+ - lib/generators/trapline/install_generator.rb
64
+ - lib/generators/trapline/templates/initializer.rb
65
+ - lib/tasks/trapline.rake
66
+ - lib/trapline.rb
67
+ - lib/trapline/cli.rb
68
+ - lib/trapline/configuration.rb
69
+ - lib/trapline/cve/client.rb
70
+ - lib/trapline/cve/record.rb
71
+ - lib/trapline/engine.rb
72
+ - lib/trapline/generator.rb
73
+ - lib/trapline/languages.rb
74
+ - lib/trapline/railtie.rb
75
+ - lib/trapline/rule.rb
76
+ - lib/trapline/runtime.rb
77
+ - lib/trapline/store.rb
78
+ - lib/trapline/suite.rb
79
+ - lib/trapline/templates.rb
80
+ - lib/trapline/templates/cwe/cwe-22.yml
81
+ - lib/trapline/templates/cwe/cwe-327.yml
82
+ - lib/trapline/templates/cwe/cwe-502.yml
83
+ - lib/trapline/templates/cwe/cwe-601.yml
84
+ - lib/trapline/templates/cwe/cwe-611.yml
85
+ - lib/trapline/templates/cwe/cwe-78.yml
86
+ - lib/trapline/templates/cwe/cwe-79.yml
87
+ - lib/trapline/templates/cwe/cwe-798.yml
88
+ - lib/trapline/templates/cwe/cwe-89.yml
89
+ - lib/trapline/templates/cwe/cwe-917.yml
90
+ - lib/trapline/templates/cwe/cwe-918.yml
91
+ - lib/trapline/templates/cwe/cwe-94.yml
92
+ - lib/trapline/validator.rb
93
+ - lib/trapline/version.rb
94
+ - lib/trapline/web/app.rb
95
+ - lib/trapline/web/assets/THIRD_PARTY_LICENSES.txt
96
+ - lib/trapline/web/assets/app.css
97
+ - lib/trapline/web/assets/app.js
98
+ - lib/trapline/web/assets/codemirror.css
99
+ - lib/trapline/web/assets/codemirror.js
100
+ - lib/trapline/web/assets/index.html
101
+ - lib/trapline/web/assets/jetbrains-mono-latin-400-normal.woff2
102
+ - lib/trapline/web/assets/jetbrains-mono-latin-600-normal.woff2
103
+ - lib/trapline/web/assets/public-sans-latin-400-normal.woff2
104
+ - lib/trapline/web/assets/public-sans-latin-500-normal.woff2
105
+ - lib/trapline/web/assets/public-sans-latin-600-normal.woff2
106
+ - lib/trapline/web/assets/public-sans-latin-700-normal.woff2
107
+ - lib/trapline/web/server.rb
108
+ homepage: https://github.com/JAugusto42/trapline
109
+ licenses:
110
+ - MIT
111
+ metadata:
112
+ source_code_uri: https://github.com/JAugusto42/trapline
113
+ bug_tracker_uri: https://github.com/JAugusto42/trapline/issues
114
+ rubygems_mfa_required: 'true'
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '3.3'
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ requirements:
130
+ - opengrep or semgrep on PATH to run rules
131
+ rubygems_version: 3.5.22
132
+ signing_key:
133
+ specification_version: 4
134
+ summary: Editor and generator for semgrep/opengrep rules, standalone or mounted in
135
+ Rails
136
+ test_files: []