tina4ruby 3.13.82 → 3.13.83

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: 3c8f9a8a50b4bb94a8b19b81774ce30e7a9d64fca932c2eb6a49016dc0352171
4
- data.tar.gz: 90066d82b0085d019c3118fe57d225eb78206fe44f55c235dd3e7d67d8451382
3
+ metadata.gz: 047daf112e32975b3a932f37b8cc266495c67744228dad532831ecfc918d76d1
4
+ data.tar.gz: 06a13610c6f4dca323533afda0170fe9ab8b80c8181bd21ee3cc9743314704c3
5
5
  SHA512:
6
- metadata.gz: 042b4dbfa4faf371dde9eb25e3e4406170c46086c29f1ff67afecad0fb61c482605f4f912469251b8640545142eeb295dd7eb1797081137575d941ebccf3f9be
7
- data.tar.gz: e5e303934c51d2fc83692817eef9f26382d5bbd5c1b735f6a0b3bcd65a13a7a902574b070cd86a1b60cf0e17a34788444507c5b8cd62c9d8a60aea626abff80f
6
+ metadata.gz: 9836d9caa0a14bcdebaca2effb98265c76a326c21e4d67a50daa6a22f2eef134fce9cf31ab068a60da688b2daa6fc0717a0244d58b1f17116a501a2f8ab91685
7
+ data.tar.gz: d6fae651771e754bf8a3bb5fd9a73aee386765bf0574abb285a1eccea1fefe294e96d1e15913b3b413f380d2c239881b2cb70431956a67bb3282d132068c97f2
data/CHANGELOG.md CHANGED
@@ -28,6 +28,29 @@ UNRELEASED work. When a version ships, its notes go to the release notes above.
28
28
  filename alignment, not an API change. Only code that bypassed the gem's own entry point with
29
29
  a direct `require "tina4/sql_translation"` needs to update the path.
30
30
 
31
+ ### Fixed
32
+
33
+ - **Security: the bundled Swagger UI static assets now honour the swagger gate.** `/swagger`,
34
+ `/swagger/`, `/swagger/index.html` and `/swagger/oauth2-redirect.html` were served from the
35
+ framework's own public directory BEFORE route matching (with directory-index resolution turning
36
+ `/swagger` into `swagger/index.html`), so a production server with `TINA4_SWAGGER_ENABLED=false`
37
+ still served the whole UI while `/swagger/openapi.json` correctly 404'd. Static serving now checks
38
+ the gate before it resolves an index. Bite-verified lock-in test. (python#97)
39
+ - **The startup banner advertises only a surface that answers.** The `Swagger:` and `Dashboard:`
40
+ rows printed unconditionally, so a production log claimed a dev surface was exposed and a
41
+ developer following the link hit a 404. Each row is now built by one pure helper of
42
+ (port, swagger_enabled, dev_admin_enabled), unit tested rather than inferred from stdout.
43
+ (python#99)
44
+ - **MQTT TLS tests verify the CA before trusting it.** A stale CA file in the shared temp directory
45
+ made six TLS tests FAIL instead of skip, in all four frameworks, pointing at correct TLS code.
46
+ The suites now confirm the CA actually validates the broker certificate before treating the TLS
47
+ environment as present. (python#98)
48
+ - **The gemspec declares `logger` and `base64`.** Ruby 4 dropped both from the default gems.
49
+ `tina4ruby` requires `logger` and nothing in its transitive closure provided it, so a fresh
50
+ install on Ruby 4 could fail at require time. `base64` is satisfied through `jwt` today and is
51
+ declared directly so a change there cannot break us.
52
+
53
+
31
54
  ## Earlier history (pre-3.x)
32
55
 
33
56
  Kept for reference only. The versions below are from the 0.x line, long before the unified
@@ -418,6 +418,19 @@ module Tina4
418
418
  def try_static(path, env = nil)
419
419
  return nil if path.include?("..")
420
420
 
421
+ # The framework ships the Swagger UI as STATIC assets under
422
+ # lib/tina4/public/swagger/. Static serving is independent of the gated
423
+ # /swagger handler, so without this check the UI stays reachable in
424
+ # production via '/swagger/index.html' or '/swagger/oauth2-redirect.html'
425
+ # even when swagger is disabled -- silently bypassing
426
+ # TINA4_SWAGGER_ENABLED / TINA4_DEBUG. (A bare '/swagger' and '/swagger/'
427
+ # are already intercepted by the gated handler, which is why this leak hid.)
428
+ normalised_path = path.start_with?("/") ? path : "/#{path}"
429
+ if (normalised_path == "/swagger" || normalised_path.start_with?("/swagger/")) &&
430
+ !Tina4::Swagger.enabled?
431
+ return nil
432
+ end
433
+
421
434
  @static_roots.each do |root|
422
435
  full_path = File.join(root, path)
423
436
  if File.file?(full_path)
data/lib/tina4/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tina4
4
- VERSION = "3.13.82"
4
+ VERSION = "3.13.83"
5
5
  end
data/lib/tina4.rb CHANGED
@@ -156,6 +156,25 @@ module Tina4
156
156
  # models with a Symbol/String `self.db` resolve against it.
157
157
  def databases = (@databases ||= {})
158
158
 
159
+ # Build the startup banner's optional surface lines (issue #99).
160
+ #
161
+ # Only advertise a surface that is actually REACHABLE. In production, or with
162
+ # TINA4_DEBUG off, /swagger and /__dev return 404 -- printing them anyway
163
+ # both misleads an operator into believing a dev surface is exposed and sends
164
+ # a developer to a dead link.
165
+ #
166
+ # Kept as a pure function of (port, two booleans) so the contract is unit
167
+ # testable without booting a server and grepping stdout. Parity: Python
168
+ # banner_surface_lines, PHP App::bannerSurfaceLines, Node bannerSurfaceLines.
169
+ #
170
+ # @return [Array<String>] zero, one or two ready-to-puts banner rows.
171
+ def banner_surface_lines(port, swagger_enabled:, dev_admin_enabled:)
172
+ lines = []
173
+ lines << " Swagger: http://localhost:#{port}/swagger" if swagger_enabled
174
+ lines << " Dashboard: http://localhost:#{port}/__dev" if dev_admin_enabled
175
+ lines
176
+ end
177
+
159
178
  def print_banner(host: "0.0.0.0", port: 7147, server_name: nil)
160
179
  # TINA4_SUPPRESS — short-circuit ALL banner output for headless / CI runs.
161
180
  return if Tina4::Env.is_truthy(ENV["TINA4_SUPPRESS"])
@@ -187,8 +206,12 @@ module Tina4
187
206
  puts " Simple. Fast. Human. | Built for AI. Built for you."
188
207
  puts ""
189
208
  puts " Server: http://#{display}:#{port} (#{server_name})"
190
- puts " Swagger: http://localhost:#{port}/swagger"
191
- puts " Dashboard: http://localhost:#{port}/__dev"
209
+ # Only advertise a surface that is actually reachable (issue #99).
210
+ banner_surface_lines(
211
+ port,
212
+ swagger_enabled: Tina4::Swagger.enabled?,
213
+ dev_admin_enabled: is_debug
214
+ ).each { |line| puts line }
192
215
  puts " Debug: #{is_debug ? 'ON' : 'OFF'} (Log level: #{log_level})"
193
216
  puts ""
194
217
  rescue
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tina4ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.13.82
4
+ version: 3.13.83
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tina4 Team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-23 00:00:00.000000000 Z
11
+ date: 2026-07-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -136,6 +136,34 @@ dependencies:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
138
  version: '1.8'
139
+ - !ruby/object:Gem::Dependency
140
+ name: logger
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '1.6'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '1.6'
153
+ - !ruby/object:Gem::Dependency
154
+ name: base64
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '0.2'
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '0.2'
139
167
  - !ruby/object:Gem::Dependency
140
168
  name: sqlite3
141
169
  requirement: !ruby/object:Gem::Requirement