anomonitor 0.6.1

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 (65) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +90 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +275 -0
  5. data/RELEASE.md +32 -0
  6. data/Rakefile +13 -0
  7. data/SECURITY.md +24 -0
  8. data/app/assets/config/anomonitor_manifest.js +1 -0
  9. data/app/assets/stylesheets/anomonitor/application.css +370 -0
  10. data/app/controllers/anomonitor/anomalies_controller.rb +44 -0
  11. data/app/controllers/anomonitor/application_controller.rb +21 -0
  12. data/app/controllers/anomonitor/dashboard_controller.rb +69 -0
  13. data/app/controllers/anomonitor/jobs_controller.rb +61 -0
  14. data/app/controllers/anomonitor/metrics_controller.rb +18 -0
  15. data/app/controllers/anomonitor/mutes_controller.rb +41 -0
  16. data/app/helpers/anomonitor/application_helper.rb +71 -0
  17. data/app/models/anomonitor/anomaly.rb +105 -0
  18. data/app/models/anomonitor/application_record.rb +5 -0
  19. data/app/models/anomonitor/metric_sample.rb +25 -0
  20. data/app/models/anomonitor/mute.rb +56 -0
  21. data/app/views/anomonitor/anomalies/index.html.erb +54 -0
  22. data/app/views/anomonitor/anomalies/show.html.erb +54 -0
  23. data/app/views/anomonitor/dashboard/show.html.erb +124 -0
  24. data/app/views/anomonitor/jobs/index.html.erb +135 -0
  25. data/app/views/anomonitor/metrics/index.html.erb +33 -0
  26. data/app/views/anomonitor/mutes/index.html.erb +56 -0
  27. data/app/views/layouts/anomonitor/application.html.erb +39 -0
  28. data/config/routes.rb +15 -0
  29. data/db/migrate/20260807100000_create_anomonitor_metric_samples.rb +17 -0
  30. data/db/migrate/20260807100001_create_anomonitor_anomalies.rb +23 -0
  31. data/db/migrate/20260808100000_add_resolved_at_to_anomonitor_anomalies.rb +6 -0
  32. data/db/migrate/20260808120000_create_anomonitor_mutes.rb +16 -0
  33. data/lib/anomonitor/collectors/base.rb +25 -0
  34. data/lib/anomonitor/collectors/delayed_job.rb +55 -0
  35. data/lib/anomonitor/collectors/schema_drift.rb +144 -0
  36. data/lib/anomonitor/collectors/sidekiq.rb +47 -0
  37. data/lib/anomonitor/collectors/solid_queue.rb +57 -0
  38. data/lib/anomonitor/collectors/table.rb +116 -0
  39. data/lib/anomonitor/configuration.rb +184 -0
  40. data/lib/anomonitor/configuration_validator.rb +40 -0
  41. data/lib/anomonitor/detector.rb +195 -0
  42. data/lib/anomonitor/digester.rb +79 -0
  43. data/lib/anomonitor/engine.rb +35 -0
  44. data/lib/anomonitor/jobs/browser.rb +89 -0
  45. data/lib/anomonitor/jobs/browsers/delayed_job.rb +143 -0
  46. data/lib/anomonitor/jobs/browsers/sidekiq.rb +164 -0
  47. data/lib/anomonitor/jobs/browsers/solid_queue.rb +149 -0
  48. data/lib/anomonitor/jobs/browsers/table.rb +206 -0
  49. data/lib/anomonitor/jobs/row.rb +30 -0
  50. data/lib/anomonitor/metric_point.rb +42 -0
  51. data/lib/anomonitor/metrics_export.rb +55 -0
  52. data/lib/anomonitor/notifiers/callable.rb +44 -0
  53. data/lib/anomonitor/notifiers/composite.rb +33 -0
  54. data/lib/anomonitor/notifiers/rate_limited.rb +44 -0
  55. data/lib/anomonitor/notifiers/webhook.rb +108 -0
  56. data/lib/anomonitor/notifiers.rb +59 -0
  57. data/lib/anomonitor/poll_lock.rb +67 -0
  58. data/lib/anomonitor/poller.rb +164 -0
  59. data/lib/anomonitor/tenancy.rb +46 -0
  60. data/lib/anomonitor/version.rb +3 -0
  61. data/lib/anomonitor.rb +52 -0
  62. data/lib/generators/anomonitor/install/install_generator.rb +27 -0
  63. data/lib/generators/anomonitor/install/templates/anomonitor.rb +87 -0
  64. data/lib/tasks/anomonitor_tasks.rake +27 -0
  65. metadata +126 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6598d41498f36ba18da8391e8dff7dd940532ca218cbe5240357d496ed24c71a
4
+ data.tar.gz: 5064d81305a4c871b0ccaa1d6ab398c316ff09fc5a7ea6511ceb771f584c15c6
5
+ SHA512:
6
+ metadata.gz: 16534086abd65a8fb0e1888440e96dc1b4aafd146743489e05bc854c38b09f4a388e3458a14fa4b228a9c842a522259334c165c253d578d75b9b2d55101a4eca
7
+ data.tar.gz: eef964fa8914f22ff0a0fc031423f3a0a2fd3c5be6064018bedf07a39fb10dff28afb513175bd18a3edd08eb90f95e7188a44e3223e9a9d335b18cb22d57cac4
data/CHANGELOG.md ADDED
@@ -0,0 +1,90 @@
1
+ # Changelog
2
+
3
+ ## 0.6.1
4
+
5
+ - CI: allow `sqlite3 >= 2.1` for Rails 8; matrix Rails 7.1 / 7.2 / 8.0
6
+
7
+ ## 0.6.0
8
+
9
+ - Mutes / snoozes (`anomonitor_mutes` + `/mutes` UI + `c.mute(...)`)
10
+ - Anomaly digest mode (`c.digest_interval`) → `anomaly.digest` webhook batch
11
+ - Alert `match:` filters (e.g. per-queue Sidekiq: `match: { queue: true }`)
12
+ - Manual resolve emits `anomaly.acked` (auto-clear still `anomaly.resolved`)
13
+ - Notifier rate limit (`c.notifier_rate_limit`)
14
+ - Jobs browser expandable detail (args / handler / error)
15
+ - Overview tenant filter + by-tenant summary
16
+ - SchemaDrift collector tests + engine route surface tests
17
+ - RubyGems release notes (`RELEASE.md`) + gemspec MFA metadata
18
+
19
+ ## 0.5.0
20
+
21
+ - Manual resolve/ack and reopen on anomaly detail (sticky silence until clear)
22
+ - Retry failed webhooks from UI and `rails anomonitor:retry_webhooks`
23
+ - Full schema-drift `items_list` in tags (detail page shows all items)
24
+ - Boot config warnings (`ConfigurationValidator`)
25
+ - Jobs browser search (`q`) by job class/name
26
+ - Metrics export: `/anomonitor/metrics.json` and `/anomonitor/metrics.prom`
27
+ - Poll advisory/file lock (`c.poll_lock`, default on) + cooperative thread stop
28
+ - Spike baselines sample more candidates before tag filter
29
+ - Dedupe Solid Queue / status-table metrics (single `queue_depth`)
30
+ - Remove unused `ApplicationJob`
31
+
32
+ ## 0.4.1
33
+
34
+ - Pluggable `c.notifier` (callable / `#deliver` / array) + `Anomonitor::Notifiers.payload` for host transports like `Webhook::Broadcast`
35
+
36
+ ## 0.4.0
37
+
38
+ - Absolute webhook dashboard URLs via `dashboard_base_url` / `ANOMONITOR_DASHBOARD_BASE_URL`
39
+ - Sticky schema drift: `anomaly.resolved` webhook when drift clears; anomalies UI open/resolved filter
40
+ - Fingerprint full item sets (`items_digest`) so changes beyond the first 25 still re-alert
41
+ - Jobs browser: Delayed Job / table `pending` matches collectors (`run_at <= now`); hide `locked` for status-style tables
42
+ - Dashboard auth hook (`c.authenticate`) + docs / SECURITY note
43
+ - Default install alerts for `failed` and `latency`
44
+ - `schema_drift_interval` (default 15m) so drift polls less often than queue metrics
45
+ - Prune old anomalies with `retention_days` (keep open schema-drift rows)
46
+ - GitHub Actions CI (`rake test` on Ruby 3.1–3.3)
47
+
48
+ ## 0.3.2
49
+
50
+ - Schema drift alerts are sticky: notify once, stay silent until the drift clears or the item set changes (no 15‑minute re-alerts)
51
+ - Anomalies gain `resolved_at` for sticky resolution
52
+ - Jobs browser: honor `queue` filter for custom tables and Delayed Job
53
+
54
+ ## 0.3.1
55
+
56
+ - Update slack text ```"<#{dashboard}|Open anomaly>" -> "<#{dashboard}>"```
57
+
58
+ ## 0.3.0
59
+
60
+ - Jobs dashboard: collector health cards + read-only live job browser for Sidekiq, Delayed Job, Solid Queue, and custom tables
61
+
62
+ ## 0.2.3
63
+
64
+ - Wrap long metric/anomaly tag lines so tables stay within the viewport
65
+
66
+ ## 0.2.2
67
+
68
+ - Stop auto-appending engine migrations (use `rails anomonitor:install:migrations`) so Apartment hosts can own public-only copies without duplicate migration names
69
+
70
+ ## 0.2.1
71
+
72
+ - Support Rails `>= 6.1` (migrations use `ActiveRecord::Migration[6.1]`)
73
+ - Dashboard cron mode UI (mode/schedule/last run from samples)
74
+
75
+ ## 0.2.0
76
+
77
+ - Multi-tenancy: `tenants`, `exclude_tenants`, `tenant_switch` (Apartment-aware by default)
78
+ - Per-tenant Delayed Job metrics when tenants are configured
79
+ - Schema drift collector (`missing_tables` / `extra_tables` / `missing_columns` / `extra_columns`)
80
+ - Configurable `schema_drift_exclude` globs
81
+ - Custom tables: `style: :delayed_job` and optional `tenant:` for per-tenant index tables
82
+ - Tenant-aware cooldown keys, spike baselines, and Slack webhook text
83
+ - `poll_mode` `:thread` (default) or `:cron` — cron uses `rails anomonitor:poll`
84
+
85
+ ## 0.1.0
86
+
87
+ - Initial release: Sidekiq, Delayed Job, Solid Queue, and custom table collectors
88
+ - Threshold and growth-spike detection with cooldown
89
+ - Webhook notifier
90
+ - Mountable dashboard (overview, anomalies, metrics)
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright flori-s
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,275 @@
1
+ # Anomonitor
2
+
3
+ Rails engine gem that watches **Sidekiq**, **Delayed Job**, **Solid Queue**, and **custom job tables** for anomalies — threshold breaches, growth spikes, and **schema drift** — then notifies via **webhook** and shows an ops **dashboard**.
4
+
5
+ <img width="1512" height="836" alt="anomonitor" src="https://github.com/user-attachments/assets/62938225-02a7-4e5c-8baf-d8ec24658a79" />
6
+
7
+ ## Install
8
+
9
+ ```ruby
10
+ # Gemfile
11
+ gem "anomonitor", github: "flori-s/anomonitor"
12
+ ```
13
+
14
+ Requires **Ruby >= 3.0** and **Rails >= 6.1**.
15
+
16
+ ```bash
17
+ bundle install
18
+ rails generate anomonitor:install
19
+ rails anomonitor:install:migrations
20
+ rails db:migrate
21
+ ```
22
+
23
+ Mount the engine:
24
+
25
+ ```ruby
26
+ # config/routes.rb
27
+ mount Anomonitor::Engine => "/anomonitor"
28
+ ```
29
+
30
+ ## Configure
31
+
32
+ `config/initializers/anomonitor.rb`:
33
+
34
+ ```ruby
35
+ Anomonitor.configure do |c|
36
+ c.webhook_url = ENV.fetch("ANOMONITOR_WEBHOOK_URL")
37
+ c.dashboard_base_url = ENV["ANOMONITOR_DASHBOARD_BASE_URL"] # e.g. "https://ops.example.com"
38
+ c.poll_interval = 60
39
+ c.cooldown = 15 * 60
40
+ c.retention_days = 7
41
+ c.schema_drift_interval = 15 * 60
42
+
43
+ c.collectors.sidekiq = true
44
+ c.collectors.delayed_job = true
45
+ c.collectors.solid_queue = true
46
+
47
+ c.alert :queue_depth, max: 1_000
48
+ c.alert :failed, max: 50
49
+ c.alert :latency, max: 120
50
+ c.alert :growth_spike, window: 5 * 60, multiplier: 3.0
51
+ end
52
+ ```
53
+
54
+ Per-queue Sidekiq (tagged `queue_depth` / `latency` points):
55
+
56
+ ```ruby
57
+ c.alert :queue_depth, max: 250, match: { queue: true }
58
+ c.alert :latency, max: 60, match: { queue: "mailers" }
59
+ ```
60
+
61
+ `match` values: `true` = tag present, `nil`/`false` = tag absent, string = exact.
62
+
63
+ ### Mutes
64
+
65
+ ```ruby
66
+ Anomonitor.config.mute(metric: "extra_tables", tenant: "acme", duration: 24.hours, reason: "migration")
67
+ ```
68
+
69
+ Or use `/anomonitor/mutes` in the dashboard.
70
+
71
+ ### Digests
72
+
73
+ ```ruby
74
+ c.digest_interval = 3600 # batch detections into anomaly.digest
75
+ c.notifier_rate_limit = 30 # max delivers per minute
76
+ ```
77
+
78
+ ### Dashboard auth
79
+
80
+ The engine is **open by default**. Protect it:
81
+
82
+ ```ruby
83
+ Anomonitor.configure do |c|
84
+ c.authenticate = -> {
85
+ authenticate_or_request_with_http_basic("Anomonitor") do |user, pass|
86
+ ActiveSupport::SecurityUtils.secure_compare(user, ENV.fetch("ANOMONITOR_USER")) &&
87
+ ActiveSupport::SecurityUtils.secure_compare(pass, ENV.fetch("ANOMONITOR_PASSWORD"))
88
+ end
89
+ }
90
+ end
91
+ ```
92
+
93
+ Or reuse host auth:
94
+
95
+ ```ruby
96
+ c.authenticate = -> { redirect_to main_app.root_path unless current_user&.admin? }
97
+ ```
98
+
99
+ You can also constrain the mount in routes (`authenticate :user, ->(u) { u.admin? } do ... end`).
100
+
101
+ ### Multi-tenancy + schema drift
102
+
103
+ For Apartment / schema-per-tenant apps, set `tenants` so Delayed Job is collected **per tenant** and enable schema drift (**PostgreSQL** / `information_schema`):
104
+
105
+ ```ruby
106
+ Anomonitor.configure do |c|
107
+ c.tenants = -> { CustomerTenant.pluck(:name) }
108
+ c.exclude_tenants = %w[public]
109
+ # optional — defaults to Apartment::Tenant.switch when Apartment is loaded
110
+ c.tenant_switch = ->(name, &block) { Apartment::Tenant.switch(name, &block) }
111
+
112
+ c.collectors.delayed_job = true
113
+ c.collectors.schema_drift = true
114
+ c.schema_drift_interval = 15 * 60 # heavier than queue polls; default 15m
115
+ c.schema_drift_exclude = %w[
116
+ schema_migrations
117
+ ar_internal_metadata
118
+ anomonitor_*
119
+ a*
120
+ conv_*
121
+ ]
122
+
123
+ c.alert :queue_depth, max: 1_000
124
+ c.alert :missing_tables, max: 0
125
+ c.alert :extra_tables, max: 0
126
+ c.alert :missing_columns, max: 0
127
+ c.alert :extra_columns, max: 0
128
+ end
129
+ ```
130
+
131
+ `schema_drift_exclude` accepts exact names or `File.fnmatch` globs (`a*` matches all a-prefixed tables; `a_*` only matches `a_…`).
132
+
133
+ Schema drift webhooks are **sticky**: one `anomaly.detected` per tenant/metric/item-set, silence until clear or the set changes, then `anomaly.resolved` when it clears. Queue threshold and growth alerts still use `c.cooldown` (default 15 minutes).
134
+
135
+ ### Custom tables
136
+
137
+ Status-based queues:
138
+
139
+ ```ruby
140
+ c.table :customer_jobs do |t|
141
+ t.model = "Job"
142
+ t.timestamp = :created_at
143
+ t.status = :status
144
+ t.active = %w[pending running]
145
+ end
146
+ ```
147
+
148
+ Cross-tenant Delayed Job index (host provides an AR model, e.g. `SuppJob` → `public.supp_jobs`):
149
+
150
+ ```ruby
151
+ c.table :supp_jobs do |t|
152
+ t.model = "SuppJob"
153
+ t.style = :delayed_job # failed_at / locked_at / run_at / locked_by
154
+ t.tenant = :tenant # emit per-tenant metrics
155
+ end
156
+ ```
157
+
158
+ ### Webhooks
159
+
160
+ Built-in: set `ANOMONITOR_WEBHOOK_URL`. Set `ANOMONITOR_DASHBOARD_BASE_URL` (or `c.dashboard_base_url`) so Slack/JSON links are absolute.
161
+
162
+ **Custom notifier** (e.g. your app’s `Webhook::Broadcast` proxy) — replaces the built-in HTTP client:
163
+
164
+ ```ruby
165
+ Anomonitor.configure do |c|
166
+ c.notifier = ->(anomaly, event:) {
167
+ Webhook::Broadcast.new(
168
+ urls: [{ url: ENV.fetch("ANOMONITOR_DEST_URL"), headers: [
169
+ { name: "Content-Type", value: "application/json" }
170
+ ] }],
171
+ message: Anomonitor::Notifiers.payload(anomaly, event: event)
172
+ ).call
173
+ }
174
+ end
175
+ ```
176
+
177
+ Callables may return `true`/`false`, or a Hash with `:accepted` (Broadcast’s return value). Objects with `#deliver(anomaly, event:)` also work; pass an Array to fan out to several notifiers.
178
+
179
+ Slack Incoming Webhooks (`hooks.slack.com`) get a formatted `text` payload when using the built-in notifier. Other built-in URLs receive JSON:
180
+
181
+ ```json
182
+ {
183
+ "gem": "anomonitor",
184
+ "event": "anomaly.detected",
185
+ "severity": "high",
186
+ "rule": "growth_spike",
187
+ "source": "delayed_job",
188
+ "metric": "queue_depth",
189
+ "value": 4200,
190
+ "threshold": 1000,
191
+ "sampled_at": "2026-08-07T09:00:00Z",
192
+ "resolved_at": null,
193
+ "dashboard_url": "https://ops.example.com/anomonitor/anomalies/123",
194
+ "tags": { "tenant": "acme" }
195
+ }
196
+ ```
197
+
198
+ Events: `anomaly.detected` and `anomaly.resolved` (sticky schema drift clear).
199
+
200
+ ## Polling: thread vs cron
201
+
202
+ Anomonitor can collect on a background thread **or** via an external scheduler.
203
+
204
+ | `poll_mode` | Behavior |
205
+ |---|---|
206
+ | `:thread` (default) | Starts an in-process poller after boot (`poll_interval` seconds). Avoid with multi-worker Puma/Unicorn unless only one worker should run it. Prefer `:cron` in multi-worker setups. |
207
+ | `:cron` | No background thread. Schedule `rails anomonitor:poll` yourself. |
208
+
209
+ ```ruby
210
+ # In-process (default)
211
+ c.poll_mode = :thread
212
+
213
+ # Cron / systemd / Kubernetes CronJob / whenever
214
+ c.poll_mode = :cron
215
+ ```
216
+
217
+ Cron examples:
218
+
219
+ ```cron
220
+ * * * * * cd /app && bin/rails anomonitor:poll
221
+ ```
222
+
223
+ ```ruby
224
+ # config/schedule.rb (whenever)
225
+ every 1.minute do
226
+ rake "anomonitor:poll"
227
+ end
228
+ ```
229
+
230
+ `c.auto_start = false` is still supported and is equivalent to `poll_mode = :cron`.
231
+
232
+ Samples and resolved anomalies older than `retention_days` are pruned (open schema-drift rows are kept until resolved).
233
+
234
+ ## Dashboard
235
+
236
+ Open `/anomonitor` for:
237
+
238
+ - Current metric cards and mini history bars
239
+ - **Jobs** — per-collector health plus a read-only live job browser (filter by source, status, tenant, queue, search; expandable detail)
240
+ - Open anomalies (filter open / resolved / all) + resolve/ack, reopen, retry webhook
241
+ - **Mutes** — snooze by metric/rule/source/tenant
242
+ - Overview tenant filter
243
+ - Poller health (last run, collector status)
244
+
245
+ ### Metrics export
246
+
247
+ - JSON: `/anomonitor/metrics.json`
248
+ - Prometheus text: `/anomonitor/metrics.prom`
249
+
250
+ ### Retry failed webhooks
251
+
252
+ ```bash
253
+ rails anomonitor:retry_webhooks
254
+ ```
255
+
256
+ `c.poll_lock = true` (default) skips overlapping ticks via Postgres advisory lock or a tmp file lock. Prefer `poll_mode = :cron` in multi-worker setups.
257
+
258
+ ## Manual poll
259
+
260
+ ```bash
261
+ rails anomonitor:poll
262
+ ```
263
+
264
+ With Apartment, the rake task switches to the first `exclude_tenants` entry (default `public`) before collecting.
265
+
266
+ ## Development
267
+
268
+ ```bash
269
+ bundle install
270
+ bundle exec rake test
271
+ ```
272
+
273
+ ## License
274
+
275
+ MIT
data/RELEASE.md ADDED
@@ -0,0 +1,32 @@
1
+ # Releasing Anomonitor
2
+
3
+ ## Version bump
4
+
5
+ 1. Update `lib/anomonitor/version.rb`
6
+ 2. Add a section to `CHANGELOG.md`
7
+ 3. Commit and tag: `git tag v0.6.1 && git push origin v0.6.1`
8
+
9
+ ## RubyGems
10
+
11
+ Requires owner credentials on rubygems.org for the `anomonitor` gem.
12
+
13
+ ```bash
14
+ gem build anomonitor.gemspec
15
+ gem push anomonitor-0.6.1.gem
16
+ ```
17
+
18
+ Or:
19
+
20
+ ```bash
21
+ bundle exec rake release
22
+ ```
23
+
24
+ (if a `release` task is configured via bundler gem tasks)
25
+
26
+ ## GitHub-only installs
27
+
28
+ Until published, hosts can use:
29
+
30
+ ```ruby
31
+ gem "anomonitor", github: "flori-s/anomonitor", tag: "v0.6.1"
32
+ ```
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rake"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ t.warning = false
11
+ end
12
+
13
+ task default: :test
data/SECURITY.md ADDED
@@ -0,0 +1,24 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ | Version | Supported |
6
+ | ------- | ------------------ |
7
+ | 0.6.x | :white_check_mark: |
8
+ | 0.5.x | :white_check_mark: |
9
+ | 0.4.x | :white_check_mark: |
10
+ | 0.3.x | :white_check_mark: |
11
+ | < 0.3 | :x: |
12
+
13
+ ## Dashboard access
14
+
15
+ The Anomonitor engine is **not authenticated by default**. If you mount it on a publicly reachable host, set `c.authenticate` in the initializer (HTTP basic or your app’s admin check). See the README.
16
+
17
+ ## Reporting a Vulnerability
18
+
19
+ Please report security vulnerabilities privately. Do **not** open a public issue.
20
+
21
+ - Prefer GitHub Security Advisories for [flori-s/anomonitor](https://github.com/flori-s/anomonitor/security/advisories/new) if available
22
+ - Or email the maintainer via the contact listed in the gemspec / GitHub profile
23
+
24
+ You can expect an initial response within **7 days**. If the report is accepted, we will work on a fix and coordinate disclosure. If declined, we will explain why.
@@ -0,0 +1 @@
1
+ //= link_directory ../stylesheets/anomonitor .css