ideasbugs 0.10.1 → 1.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d3b0669f68f65a7cef3986dac6a2ca57938b1d38942338283593e026003e2688
4
- data.tar.gz: ed01d83195291ee4cdc81bbf43f043d520c772155780c14a9e070fe0c1e4a2a4
3
+ metadata.gz: 76f8be375e72b7a33a5f0f5b6bc0fc895ced476aeb3b5325a950fa0a7bd01475
4
+ data.tar.gz: 565e8e8d07ea9c2604748e88774ebe67bec1473648110b97f0c797911b72127d
5
5
  SHA512:
6
- metadata.gz: 866b49d6047d00eaef7b79bf419fd8d0762e7ab1a5c1ea45c04b6cb121aa7f929c7049ca41bd9dfe7759bedf2fb46d69d6b8b017cf5e612a6be5046008472217
7
- data.tar.gz: 8f25c5e661173324bce15de5ab9ca6e077d04da693231e3df9cf4e46f5a053062e678a2a8c82caedb2e1b47d2539b81036c97c496eb489f32888b2b1c2b0be4a
6
+ metadata.gz: a9623c31745f085489bf222390b964324391f63396d87f3cfa32714a4cbc29180a2703350571840006cc81121e92541a06224a7d831cdb407e7027f6afa9d56a
7
+ data.tar.gz: eb8cade7b5bed11612632d95e83cc828c831351721df1dfbb43ea0c54f11dbe7d6ee5f24b79c123f6b6bfcee215a1d3d2140fbd9a3b9c17fb1bdaab883dab58e
data/AGENTS.md CHANGED
@@ -5,7 +5,7 @@ Instructions for coding agents. Two audiences:
5
5
  - **[Installing ideasbugs into a Rails app](#installing-into-a-rails-app)** — you are working in a host app and were asked to add product feedback, bug reports, or a feature-request board.
6
6
  - **[Working on the gem itself](#working-on-the-gem-itself)** — you are working in this repository.
7
7
 
8
- Requirements: Ruby >= 3.2, Rails >= 7.1. Active Storage only for screenshots. The widget needs the CSRF token from `csrf_meta_tags`, which a standard Rails layout already has.
8
+ Requirements: Ruby >= 3.2, Rails >= 7.1 and < 9. Active Storage only for screenshots. The widget needs the CSRF token from `csrf_meta_tags`, which a standard Rails layout already has.
9
9
 
10
10
  If you are in a host app and this file is not in front of you, it ships inside the gem: `cat "$(bundle show ideasbugs)/AGENTS.md"`.
11
11
 
@@ -161,6 +161,11 @@ Everything is optional; a fresh install works with zero config. Full list with c
161
161
 
162
162
  ## Working on the gem itself
163
163
 
164
+ The development toolchain is pinned to Ruby 4.0.5 in `mise.toml`. Do not use
165
+ macOS's `/usr/bin/ruby`. In shells, hooks, or automation where mise activation
166
+ is uncertain, explicitly prepend the configured Ruby, for example
167
+ `PATH="$(mise where ruby)/bin:$PATH" bundle exec rake test`.
168
+
164
169
  ```bash
165
170
  bundle exec rake test # minitest, dummy app under test/dummy
166
171
  bundle exec rake test:system # browser tests, separate task
data/CHANGELOG.md CHANGED
@@ -1,5 +1,44 @@
1
1
  # Changelog
2
2
 
3
+ ## [Unreleased]
4
+
5
+ ## [1.0.1] - 2026-08-11
6
+
7
+ - **Screenshot responses now stream privately through the gated engine route.**
8
+ Responses are marked `private, no-store` and `nosniff`, so sensitive screen
9
+ captures are not buffered or left cacheable by the browser.
10
+ - **Stored page context is now privacy-filtered.** Only bounded HTTP(S) URLs
11
+ without credentials, query strings, or fragments are retained.
12
+
13
+ ## [1.0.0] - 2026-08-11
14
+
15
+ - **The documented integration surface is now the stable 1.x contract.**
16
+ `Ideasbugs.configure`, every generated configuration option,
17
+ `mount_ideasbugs`, `ideasbugs_tag`, `data-ideasbugs-open`,
18
+ `Ideasbugs::Feedback`'s documented fields/status helpers/scopes, tenant
19
+ helpers, gated screenshots, and `on_submit` will follow semantic versioning.
20
+ `Feedback` is intentional domain language and will not be renamed to `Post`.
21
+ Engine controllers, partials, CSS classes and widget internals remain private.
22
+ - **The three documented status scopes now exist.** `Feedback.open`,
23
+ `.in_review`, and `.resolved` return ordinary relations and compose with
24
+ `for_tenant`/`newest_first`. They remain hand-written scopes rather than an
25
+ enum because `open` would collide with `Kernel#open` in enum machinery.
26
+ - **Hosts that load Action View from an initializer boot normally.**
27
+ `WidgetHelper` is required from `lib` before the engine registers its Action
28
+ View hook. A dummy-host initializer reproduces that early load on every test
29
+ boot. The helper constant and `ideasbugs_tag` API are unchanged.
30
+ - Supported combinations are Ruby 3.2 through 4.0 and Rails 7.1 through 8.1.
31
+ Rails now has a `< 9` upper bound: future Rails majors must pass the suite
32
+ before Bundler treats them as supported.
33
+ - Added a security policy and explicit privacy, retention, and deletion guidance
34
+ for feedback, host-provided attribution, page context, and screenshots.
35
+ Upgrading from 0.10.1 requires no migration or initializer change.
36
+ - Release validation now runs the normal and browser suites, RuboCop, the widget
37
+ syntax check and gem build on Ruby 4.0; it verifies RubyGems indexing and
38
+ creates the GitHub Release. Generator tests now write only under ignored root
39
+ `tmp/`, so running tests before lint no longer leaves generated files in
40
+ RuboCop's input tree.
41
+
3
42
  ## 0.10.1
4
43
 
5
44
  - **The demo inbox now teaches the Ideasbugs workflow.** Its open, in-review,
data/README.md CHANGED
@@ -61,7 +61,7 @@ duplicating them.
61
61
  > The dashboard defaults to **development only**. Set `authorize_admin` before
62
62
  > you deploy — see [Configure](#configure).
63
63
 
64
- Ruby >= 3.2 · Rails >= 7.1 · Active Storage only if you want screenshots ·
64
+ Ruby >= 3.2 · Rails >= 7.1 and < 9 · Active Storage only if you want screenshots ·
65
65
  CSRF token comes from `csrf_meta_tags`, already in a standard Rails layout.
66
66
 
67
67
  Installing with a coding agent? Point it at [AGENTS.md](AGENTS.md) — the same
@@ -269,6 +269,26 @@ nothing changes until you set `config.tenant`.
269
269
 
270
270
  </details>
271
271
 
272
+ ## Compatibility and public API
273
+
274
+ The following are the model and integration contracts that 1.x will keep
275
+ stable under semantic versioning:
276
+
277
+ - `Ideasbugs::Feedback`, including `kind`, `section`, `message`, `status`,
278
+ `page_url`, `user_agent`, `tenant`, `author_id`, `author_label`, timestamps,
279
+ `STATUSES`, status helpers/scopes, `newest_first`, `for_tenant`, and gated
280
+ `screenshots`.
281
+ - `has_feedback` and the host-model collection it defines.
282
+ - `Ideasbugs.configure` and the documented configuration options.
283
+ - `mount_ideasbugs`, `ideasbugs_tag`, and `data-ideasbugs-open`.
284
+ - `config.on_submit`, called with the saved `Ideasbugs::Feedback`.
285
+
286
+ `Feedback` is intentional domain language and will not be renamed to a generic
287
+ `Post` during 1.x. Engine controllers, partials, CSS classes, generated HTML,
288
+ and widget implementation objects are private. Incompatible changes to the
289
+ public list above wait for a new major version; a deprecation normally ships
290
+ first.
291
+
272
292
  ## Localization
273
293
 
274
294
  Every string resolves through Rails I18n under `ideasbugs.*` and follows the
@@ -305,6 +325,10 @@ to `kind.humanize`.
305
325
 
306
326
  ## Security
307
327
 
328
+ See [SECURITY.md](https://github.com/yshmarov/ideasbugs/blob/main/SECURITY.md)
329
+ for supported versions, private vulnerability reporting, the complete
330
+ stored-data boundary, and deletion/retention guidance.
331
+
308
332
  - **Both gates run server-side**, on every request. The dashboard denies
309
333
  everything outside development until you configure `authorize_admin`.
310
334
  - **Screenshots stream through the dashboard's own gate** — never public Active
@@ -320,6 +344,10 @@ to `kind.humanize`.
320
344
  survives Turbo visits.
321
345
  - **No foreign key into your user table.** Attribution is loose fields, so the
322
346
  gem never couples to your user model.
347
+ - **The host owns retention and deletion.** Feedback can contain personal data,
348
+ and screenshots can contain anything visible on the user's screen. Delete a
349
+ row with `Ideasbugs::Feedback.find(id).destroy!`, then apply your object-store,
350
+ backup, cache, and export retention policies separately.
323
351
 
324
352
  ## Development
325
353
 
@@ -332,7 +360,8 @@ bundle exec rubocop
332
360
 
333
361
  Tests run against a dummy Rails app in `test/dummy`; the widget is covered by
334
362
  Capybara system tests in a real browser. CI runs Rails 7.1 / 7.2 / 8.0 / 8.1
335
- against Ruby 3.2–3.4 (per-version Gemfiles in `gemfiles/`).
363
+ against Ruby 3.2–4.0 (per-version Gemfiles in `gemfiles/`). Rails 9 is outside
364
+ the 1.x dependency contract until it passes the suite.
336
365
 
337
366
  ## One family
338
367
 
data/SECURITY.md ADDED
@@ -0,0 +1,44 @@
1
+ # Security policy
2
+
3
+ ## Supported versions
4
+
5
+ Security fixes are released for the latest 1.x version. If a report also
6
+ affects the newest 0.x release, a backport may be published when the fix is
7
+ small and the affected application cannot upgrade immediately.
8
+
9
+ ## Reporting a vulnerability
10
+
11
+ Please do not open a public issue for a vulnerability. Use
12
+ [GitHub private vulnerability reporting](https://github.com/yshmarov/ideasbugs/security/advisories/new)
13
+ and include the affected version, a minimal reproduction, impact, and any known
14
+ workaround. Credentials, session cookies, private application URLs, customer
15
+ feedback, screenshots, and production database contents should not be included.
16
+
17
+ Public disclosure should wait until a fixed version is available and affected
18
+ users have had a reasonable opportunity to upgrade.
19
+
20
+ ## Data and deployment boundary
21
+
22
+ The submission endpoint is available to requests allowed by `config.enabled`,
23
+ which defaults to everyone because production feedback collection is the gem's
24
+ purpose. Hosts decide whether to narrow that gate. The dashboard is separately
25
+ protected by `authorize_admin` and fails closed outside development until the
26
+ host explicitly grants access.
27
+
28
+ A feedback record can store its kind, section, message, status, page URL, user
29
+ agent, timestamps, an opaque tenant key, optional host-provided author id and
30
+ label, and Active Storage screenshots. A screenshot can contain any data the
31
+ user's screen displayed. Screenshots stream through the dashboard gate rather
32
+ than public blob URLs, but hosts still own the storage service, bucket policy,
33
+ backups, retention period, access controls, and incident response.
34
+
35
+ Delete feedback with ordinary Active Record when a user request or application
36
+ policy requires erasure:
37
+
38
+ ```ruby
39
+ Ideasbugs::Feedback.find(id).destroy!
40
+ ```
41
+
42
+ That removes the record and lets Rails apply the configured Active Storage
43
+ attachment lifecycle. Confirm deletion separately in object storage, replicas,
44
+ backups, caches, and exports; deleting the live row does not rewrite them.
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'uri'
4
+
3
5
  module Ideasbugs
4
6
  # Who is asking, which tenant they are in, and the gates that answer both.
5
7
  #
@@ -47,5 +49,23 @@ module Ideasbugs
47
49
  def tenant_scope
48
50
  Feedback.for_tenant(current_tenant)
49
51
  end
52
+
53
+ # Browser URLs can carry password-reset tokens, signed ids, and campaign
54
+ # details in their query or fragment. Store only a bounded HTTP(S) location
55
+ # without credentials before it reaches the feedback table.
56
+ def clean_page_url(value)
57
+ raw = value.to_s
58
+ return if raw.blank? || raw.length > 2_048
59
+
60
+ uri = URI.parse(raw)
61
+ return unless %w[http https].include?(uri.scheme&.downcase)
62
+ return if uri.host.blank? || uri.userinfo.present?
63
+
64
+ uri.query = nil
65
+ uri.fragment = nil
66
+ uri.to_s.first(255)
67
+ rescue URI::InvalidURIError
68
+ nil
69
+ end
50
70
  end
51
71
  end
@@ -7,14 +7,15 @@ module Ideasbugs
7
7
  # be reachable without passing the same gate as the dashboard — regardless of
8
8
  # how the host app configures (or doesn't configure) blob access.
9
9
  class ScreenshotsController < DashboardController
10
+ include ActiveStorage::Streaming if defined?(::ActiveStorage::Streaming)
11
+
10
12
  def show
11
13
  screenshot = Feedback.for_tenant(current_tenant)
12
14
  .find(params[:feedback_id]).screenshots.find(params[:id])
13
15
 
14
- send_data screenshot.download,
15
- filename: screenshot.filename.to_s,
16
- type: screenshot.content_type,
17
- disposition: 'inline'
16
+ response.headers['X-Content-Type-Options'] = 'nosniff'
17
+ response.headers['Cache-Control'] = 'private, no-store'
18
+ send_blob_stream screenshot.blob, disposition: 'inline'
18
19
  end
19
20
 
20
21
  private
@@ -23,6 +23,7 @@ module Ideasbugs
23
23
 
24
24
  def create
25
25
  feedback = Feedback.new(feedback_params)
26
+ feedback.page_url = clean_page_url(feedback.page_url)
26
27
  feedback.user_agent = request.user_agent
27
28
  feedback.tenant = current_tenant
28
29
  attribute_author(feedback)
@@ -29,6 +29,7 @@ module Ideasbugs
29
29
  scope :newest_first, -> { order(id: :desc) }
30
30
 
31
31
  STATUSES.each do |status|
32
+ scope status, -> { where(status:) }
32
33
  define_method(:"#{status}?") { self.status == status }
33
34
  end
34
35
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ideasbugs
4
- VERSION = '0.10.1'
4
+ VERSION = '1.0.1'
5
5
  end
@@ -3,7 +3,10 @@
3
3
  module Ideasbugs
4
4
  # Included into the host's ActionView. Drop `<%= ideasbugs_tag %>`
5
5
  # before </body> in your layout; it renders nothing unless feedback is
6
- # enabled for the request.
6
+ # enabled for the request. This lives under lib and is required before the
7
+ # engine registers its Action View load hook: a host initializer may load
8
+ # Action View before Rails sets up application autoloaders, when an
9
+ # app/helpers constant cannot be resolved yet.
7
10
  module WidgetHelper
8
11
  def ideasbugs_tag
9
12
  return unless Ideasbugs.enabled?(request)
data/lib/ideasbugs.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require 'ideasbugs/version'
4
4
  require 'ideasbugs/configuration'
5
5
  require 'ideasbugs/widget'
6
+ require 'ideasbugs/widget_helper'
6
7
  require 'ideasbugs/has_feedback'
7
8
  require 'ideasbugs/seeds'
8
9
  require 'ideasbugs/engine'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ideasbugs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.1
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yaroslav Shmarov
@@ -16,6 +16,9 @@ dependencies:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
18
  version: '7.1'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '9'
19
22
  type: :runtime
20
23
  prerelease: false
21
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -23,6 +26,9 @@ dependencies:
23
26
  - - ">="
24
27
  - !ruby/object:Gem::Version
25
28
  version: '7.1'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '9'
26
32
  description: |
27
33
  A mountable Rails engine that adds a "Send feedback" widget to your app —
28
34
  bug reports, feature requests, screenshots — and stores submissions in your
@@ -39,6 +45,7 @@ files:
39
45
  - MIT-LICENSE
40
46
  - README.md
41
47
  - Rakefile
48
+ - SECURITY.md
42
49
  - app/controllers/concerns/ideasbugs/request_context.rb
43
50
  - app/controllers/ideasbugs/application_controller.rb
44
51
  - app/controllers/ideasbugs/dashboard_controller.rb
@@ -46,7 +53,6 @@ files:
46
53
  - app/controllers/ideasbugs/screenshots_controller.rb
47
54
  - app/controllers/ideasbugs/submissions_controller.rb
48
55
  - app/controllers/ideasbugs/widgets_controller.rb
49
- - app/helpers/ideasbugs/widget_helper.rb
50
56
  - app/models/ideasbugs/application_record.rb
51
57
  - app/models/ideasbugs/feedback.rb
52
58
  - app/views/ideasbugs/feedbacks/_feedback_panel.html.erb
@@ -96,6 +102,7 @@ files:
96
102
  - lib/ideasbugs/version.rb
97
103
  - lib/ideasbugs/widget.js
98
104
  - lib/ideasbugs/widget.rb
105
+ - lib/ideasbugs/widget_helper.rb
99
106
  - lib/tasks/ideasbugs_tasks.rake
100
107
  homepage: https://github.com/yshmarov/ideasbugs
101
108
  licenses:
@@ -120,7 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
120
127
  - !ruby/object:Gem::Version
121
128
  version: '0'
122
129
  requirements: []
123
- rubygems_version: 4.0.10
130
+ rubygems_version: 4.0.16
124
131
  specification_version: 4
125
132
  summary: 'In-app product feedback collection for Rails: a drop-in widget and a built-in
126
133
  triage dashboard.'