Merge branch 'main' into mobile-columns-pt-iii

* main:
  Update Rails
  Ignore hotkeys with modifiers
  Fix 1Password account ID (was user UUID) (#2278)
  Switch 1Password account to 37signals.1password.com (#2276)
  Remove CSS testing comments
  Max card count equals geared pagination size
  Block IPv4-compatible IPv6 addresses in SSRF protection (#2273)
  Only enable transitions on user interaction
  Don't update counter if value hasn't changed
This commit is contained in:
Andy Smith
2026-01-02 14:50:27 -06:00
13 changed files with 114 additions and 64 deletions
+28 -26
View File
@@ -13,7 +13,7 @@ GIT
GIT
remote: https://github.com/rails/rails.git
revision: 4f4e0acaf558f6adf7df3ac23a51beb470463901
revision: 60d92e4e7dfe923528ccdccc18820ccfe841b7b8
branch: main
specs:
actioncable (8.2.0.alpha)
@@ -85,6 +85,7 @@ GIT
json
logger (>= 1.4.2)
minitest (>= 5.1)
psych (>= 4)
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
uri (>= 0.13.1)
@@ -115,7 +116,7 @@ GIT
GEM
remote: https://rubygems.org/
specs:
action_text-trix (2.1.15)
action_text-trix (2.1.16)
railties
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
@@ -166,20 +167,20 @@ GEM
childprocess (5.1.0)
logger (~> 1.5)
chunky_png (1.4.0)
concurrent-ruby (1.3.5)
concurrent-ruby (1.3.6)
connection_pool (2.5.5)
crack (1.0.1)
bigdecimal
rexml
crass (1.0.6)
date (3.5.0)
date (3.5.1)
debug (1.11.0)
irb (~> 1.10)
reline (>= 0.3.8)
dotenv (3.1.8)
drb (2.2.3)
ed25519 (1.4.0)
erb (6.0.0)
erb (6.0.1)
erubi (1.13.1)
et-orbi (1.4.0)
tzinfo
@@ -202,7 +203,7 @@ GEM
globalid (1.3.0)
activesupport (>= 6.1)
hashdiff (1.2.1)
i18n (1.14.7)
i18n (1.14.8)
concurrent-ruby (~> 1.0)
image_processing (1.14.0)
mini_magick (>= 4.9.5, < 6)
@@ -211,8 +212,8 @@ GEM
actionpack (>= 6.0.0)
activesupport (>= 6.0.0)
railties (>= 6.0.0)
io-console (0.8.1)
irb (1.15.3)
io-console (0.8.2)
irb (1.16.0)
pp (>= 0.6.0)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
@@ -220,7 +221,7 @@ GEM
actionview (>= 7.0.0)
activesupport (>= 7.0.0)
jmespath (1.6.2)
json (2.17.1)
json (2.18.0)
jwt (3.1.2)
base64
kamal (2.9.0)
@@ -243,7 +244,7 @@ GEM
launchy (>= 2.2, < 4)
lint_roller (1.1.0)
logger (1.7.0)
loofah (2.24.1)
loofah (2.25.0)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
mail (2.9.0)
@@ -257,7 +258,8 @@ GEM
mini_magick (5.3.1)
logger
mini_mime (1.1.5)
minitest (5.26.2)
minitest (6.0.1)
prism (~> 1.5)
mission_control-jobs (1.1.0)
actioncable (>= 7.1)
actionpack (>= 7.1)
@@ -274,7 +276,7 @@ GEM
msgpack (1.8.0)
net-http-persistent (4.0.6)
connection_pool (~> 2.2, >= 2.2.4)
net-imap (0.5.12)
net-imap (0.6.2)
date
net-protocol
net-pop (0.1.2)
@@ -289,21 +291,21 @@ GEM
net-protocol
net-ssh (7.3.0)
nio4r (2.7.5)
nokogiri (1.18.10-aarch64-linux-gnu)
nokogiri (1.19.0-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.10-aarch64-linux-musl)
nokogiri (1.19.0-aarch64-linux-musl)
racc (~> 1.4)
nokogiri (1.18.10-arm-linux-gnu)
nokogiri (1.19.0-arm-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.10-arm-linux-musl)
nokogiri (1.19.0-arm-linux-musl)
racc (~> 1.4)
nokogiri (1.18.10-arm64-darwin)
nokogiri (1.19.0-arm64-darwin)
racc (~> 1.4)
nokogiri (1.18.10-x86_64-darwin)
nokogiri (1.19.0-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.18.10-x86_64-linux-gnu)
nokogiri (1.19.0-x86_64-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.10-x86_64-linux-musl)
nokogiri (1.19.0-x86_64-linux-musl)
racc (~> 1.4)
openssl (3.3.2)
ostruct (0.6.3)
@@ -322,7 +324,7 @@ GEM
actionpack (>= 7.0.0)
activesupport (>= 7.0.0)
rack
psych (5.2.6)
psych (5.3.1)
date
stringio
public_suffix (6.0.2)
@@ -338,7 +340,7 @@ GEM
rack (>= 3.0.0)
rack-test (2.2.0)
rack (>= 1.3)
rackup (2.2.1)
rackup (2.3.1)
rack (>= 3)
rails-dom-testing (2.3.0)
activesupport (>= 5.0.0)
@@ -349,7 +351,7 @@ GEM
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
rainbow (3.1.1)
rake (13.3.1)
rdoc (6.16.1)
rdoc (7.0.3)
erb
psych (>= 4.0.0)
tsort
@@ -437,14 +439,14 @@ GEM
ostruct
stimulus-rails (1.3.4)
railties (>= 6.0.0)
stringio (3.1.9)
stringio (3.2.0)
thor (1.4.0)
thruster (0.1.17)
thruster (0.1.17-aarch64-linux)
thruster (0.1.17-arm64-darwin)
thruster (0.1.17-x86_64-darwin)
thruster (0.1.17-x86_64-linux)
timeout (0.4.4)
timeout (0.6.0)
trilogy (2.9.0)
tsort (0.2.0)
turbo-rails (2.0.20)
@@ -477,7 +479,7 @@ GEM
websocket-extensions (0.1.5)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.7.3)
zeitwerk (2.7.4)
PLATFORMS
aarch64-linux
+27 -25
View File
@@ -60,7 +60,7 @@ GIT
GIT
remote: https://github.com/rails/rails.git
revision: 4f4e0acaf558f6adf7df3ac23a51beb470463901
revision: 60d92e4e7dfe923528ccdccc18820ccfe841b7b8
branch: main
specs:
actioncable (8.2.0.alpha)
@@ -132,6 +132,7 @@ GIT
json
logger (>= 1.4.2)
minitest (>= 5.1)
psych (>= 4)
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
uri (>= 0.13.1)
@@ -183,7 +184,7 @@ PATH
GEM
remote: https://rubygems.org/
specs:
action_text-trix (2.1.15)
action_text-trix (2.1.16)
railties
activemodel-serializers-xml (1.0.3)
activemodel (>= 5.0.0.a)
@@ -244,13 +245,13 @@ GEM
childprocess (5.1.0)
logger (~> 1.5)
chunky_png (1.4.0)
concurrent-ruby (1.3.5)
concurrent-ruby (1.3.6)
connection_pool (2.5.5)
crack (1.0.1)
bigdecimal
rexml
crass (1.0.6)
date (3.5.0)
date (3.5.1)
debug (1.11.0)
irb (~> 1.10)
reline (>= 0.3.8)
@@ -258,7 +259,7 @@ GEM
drb (2.2.3)
dry-initializer (3.2.0)
ed25519 (1.4.0)
erb (6.0.0)
erb (6.0.1)
erubi (1.13.1)
et-orbi (1.4.0)
tzinfo
@@ -280,7 +281,7 @@ GEM
globalid (1.3.0)
activesupport (>= 6.1)
hashdiff (1.2.1)
i18n (1.14.7)
i18n (1.14.8)
concurrent-ruby (~> 1.0)
image_processing (1.14.0)
mini_magick (>= 4.9.5, < 6)
@@ -289,8 +290,8 @@ GEM
actionpack (>= 6.0.0)
activesupport (>= 6.0.0)
railties (>= 6.0.0)
io-console (0.8.1)
irb (1.15.3)
io-console (0.8.2)
irb (1.16.0)
pp (>= 0.6.0)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
@@ -298,7 +299,7 @@ GEM
actionview (>= 7.0.0)
activesupport (>= 7.0.0)
jmespath (1.6.2)
json (2.17.1)
json (2.18.0)
jwt (3.1.2)
base64
kamal (2.9.0)
@@ -321,7 +322,7 @@ GEM
launchy (>= 2.2, < 4)
lint_roller (1.1.0)
logger (1.7.0)
loofah (2.24.1)
loofah (2.25.0)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
mail (2.9.0)
@@ -335,7 +336,8 @@ GEM
mini_magick (5.3.1)
logger
mini_mime (1.1.5)
minitest (5.26.2)
minitest (6.0.1)
prism (~> 1.5)
mission_control-jobs (1.1.0)
actioncable (>= 7.1)
actionpack (>= 7.1)
@@ -352,7 +354,7 @@ GEM
msgpack (1.8.0)
net-http-persistent (4.0.6)
connection_pool (~> 2.2, >= 2.2.4)
net-imap (0.5.12)
net-imap (0.6.2)
date
net-protocol
net-pop (0.1.2)
@@ -367,19 +369,19 @@ GEM
net-protocol
net-ssh (7.3.0)
nio4r (2.7.5)
nokogiri (1.18.10-aarch64-linux-gnu)
nokogiri (1.19.0-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.10-aarch64-linux-musl)
nokogiri (1.19.0-aarch64-linux-musl)
racc (~> 1.4)
nokogiri (1.18.10-arm-linux-gnu)
nokogiri (1.19.0-arm-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.10-arm-linux-musl)
nokogiri (1.19.0-arm-linux-musl)
racc (~> 1.4)
nokogiri (1.18.10-arm64-darwin)
nokogiri (1.19.0-arm64-darwin)
racc (~> 1.4)
nokogiri (1.18.10-x86_64-linux-gnu)
nokogiri (1.19.0-x86_64-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.10-x86_64-linux-musl)
nokogiri (1.19.0-x86_64-linux-musl)
racc (~> 1.4)
openssl (3.3.2)
ostruct (0.6.3)
@@ -428,7 +430,7 @@ GEM
actionpack (>= 7.0.0)
activesupport (>= 7.0.0)
rack
psych (5.2.6)
psych (5.3.1)
date
stringio
public_suffix (6.0.2)
@@ -444,7 +446,7 @@ GEM
rack (>= 3.0.0)
rack-test (2.2.0)
rack (>= 1.3)
rackup (2.2.1)
rackup (2.3.1)
rack (>= 3)
rails-dom-testing (2.3.0)
activesupport (>= 5.0.0)
@@ -458,7 +460,7 @@ GEM
rake-compiler-dock (1.9.1)
rb_sys (0.9.117)
rake-compiler-dock (= 1.9.1)
rdoc (6.16.1)
rdoc (7.0.3)
erb
psych (>= 4.0.0)
tsort
@@ -556,14 +558,14 @@ GEM
ostruct
stimulus-rails (1.3.4)
railties (>= 6.0.0)
stringio (3.1.9)
stringio (3.2.0)
stripe (18.0.1)
thor (1.4.0)
thruster (0.1.17)
thruster (0.1.17-aarch64-linux)
thruster (0.1.17-arm64-darwin)
thruster (0.1.17-x86_64-linux)
timeout (0.4.4)
timeout (0.6.0)
trilogy (2.9.0)
tsort (0.2.0)
turbo-rails (2.0.20)
@@ -624,7 +626,7 @@ GEM
anyway_config (>= 1.3, < 3)
railties
yabeda (~> 0.8)
zeitwerk (2.7.3)
zeitwerk (2.7.4)
PLATFORMS
aarch64-linux
+3 -1
View File
@@ -41,7 +41,7 @@
--column-width-collapsed: 40px;
--column-width-expanded: 450px;
--progress-increment: var(--progress-max-height) / var(--progress-max-cards);
--progress-max-cards: 20;
--progress-max-cards: 15; /* should match first geared pagination page size */
--progress-max-height: 50dvh;
container-type: inline-size;
@@ -66,6 +66,7 @@
}
&:has(.cards) {
block-size: 100%;
min-block-size: 20lh;
}
@@ -420,6 +421,7 @@
&:after {
block-size: calc(var(--column-width-collapsed) + var(--card-count) * var(--progress-increment));
max-block-size: none;
opacity: 1;
inline-size: var(--column-width-collapsed);
}
@@ -10,7 +10,7 @@ export default class extends Controller {
}
handleKeydown(event) {
if (this.#shouldIgnore(event)) return
if (this.#shouldIgnore(event) || this.#hasModifier(event)) return
const handler = this.#keyHandlers[event.key.toLowerCase()]
if (handler) {
@@ -37,6 +37,10 @@ export default class extends Controller {
target.closest("input, textarea, [contenteditable], lexxy-editor")
}
#hasModifier(event) {
return event.metaKey || event.ctrlKey || event.altKey || event.shiftKey
}
get #selectedCard() {
// Find the navigable-list that currently has focus
const focusedList = this.navigableListOutlets.find(list => list.hasFocus)
@@ -5,7 +5,7 @@ export default class extends Controller {
static targets = [ "item", "counter" ]
static values = {
propertyName: String,
maxValue: { type: Number, default: 20 }
maxValue: { type: Number, default: 15 } // should match first geared pagination page size
}
initialize() {
@@ -13,7 +13,9 @@ export default class extends Controller {
}
connect() {
this.#updateCounter()
if (this.itemTargets.length > 0) {
this.#updateCounter()
}
}
itemTargetConnected() {
+9 -3
View File
@@ -16,13 +16,19 @@ module SsrfProtection
def resolve_public_ip(hostname)
ip_addresses = resolve_dns(hostname)
public_ips = ip_addresses.reject { |ip| private_address?(ip) }
public_ips = ip_addresses.reject { |ip| blocked_address?(ip) }
public_ips.sort_by { |ipaddr| ipaddr.ipv4? ? 0 : 1 }.first&.to_s
end
def private_address?(ip)
def blocked_address?(ip)
ip = IPAddr.new(ip.to_s) unless ip.is_a?(IPAddr)
ip.private? || ip.loopback? || ip.link_local? || ip.ipv4_mapped? || in_disallowed_range?(ip)
ip.private? ||
ip.loopback? ||
ip.link_local? ||
ip.ipv4_mapped? ||
ip.ipv4_compat? ||
in_disallowed_range?(ip)
end
private
+1 -1
View File
@@ -1,5 +1,5 @@
<button class="cards__expander btn btn--plain" data-collapsible-columns-target="button" data-css-variable-counter-target="counter" data-action="click->collapsible-columns#toggle click->navigable-list#selectCurrentOrReset"
style="--card-count: <%= [ count, 20 ].min %>" aria-controls="<%= column_id %>" aria-expanded="false">
style="--card-count: <%= [ count, 15 ].min %>" aria-controls="<%= column_id %>" aria-expanded="false">
<span class="cards__expander-count" data-drag-and-drop-counter="true"><%= count > 99 ? "99+" : count %></span>
<h2 class="cards__expander-title" data-collapsible-columns-target="title">
<%= title %>
+1 -1
View File
@@ -1,4 +1,4 @@
SECRETS=$(kamal secrets fetch --adapter 1password --account basecamp --from Deploy/Fizzy Deployments/BASECAMP_REGISTRY_PASSWORD Deployments/DASH_BASIC_AUTH_SECRET Beta/RAILS_MASTER_KEY Beta/MYSQL_ALTER_PASSWORD Beta/MYSQL_ALTER_USER Beta/MYSQL_APP_PASSWORD Beta/MYSQL_APP_USER Beta/MYSQL_READONLY_PASSWORD Beta/MYSQL_READONLY_USER Beta/SECRET_KEY_BASE Beta/VAPID_PUBLIC_KEY Beta/VAPID_PRIVATE_KEY Beta/ACTIVE_STORAGE_ACCESS_KEY_ID Beta/ACTIVE_STORAGE_SECRET_ACCESS_KEY Beta/QUEENBEE_API_TOKEN Beta/SIGNAL_ID_SECRET Beta/SENTRY_DSN Beta/ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY Beta/ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY Beta/ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT Beta/STRIPE_MONTHLY_V1_PRICE_ID Beta/STRIPE_MONTHLY_EXTRA_STORAGE_V1_PRICE_ID Beta/STRIPE_SECRET_KEY Beta/STRIPE_WEBHOOK_SECRET)
SECRETS=$(kamal secrets fetch --adapter 1password --account 23QPQDKZC5BKBIIG7UGT5GR5RM --from Deploy/Fizzy Deployments/BASECAMP_REGISTRY_PASSWORD Deployments/DASH_BASIC_AUTH_SECRET Beta/RAILS_MASTER_KEY Beta/MYSQL_ALTER_PASSWORD Beta/MYSQL_ALTER_USER Beta/MYSQL_APP_PASSWORD Beta/MYSQL_APP_USER Beta/MYSQL_READONLY_PASSWORD Beta/MYSQL_READONLY_USER Beta/SECRET_KEY_BASE Beta/VAPID_PUBLIC_KEY Beta/VAPID_PRIVATE_KEY Beta/ACTIVE_STORAGE_ACCESS_KEY_ID Beta/ACTIVE_STORAGE_SECRET_ACCESS_KEY Beta/QUEENBEE_API_TOKEN Beta/SIGNAL_ID_SECRET Beta/SENTRY_DSN Beta/ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY Beta/ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY Beta/ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT Beta/STRIPE_MONTHLY_V1_PRICE_ID Beta/STRIPE_MONTHLY_EXTRA_STORAGE_V1_PRICE_ID Beta/STRIPE_SECRET_KEY Beta/STRIPE_WEBHOOK_SECRET)
GITHUB_TOKEN=$(gh config get -h github.com oauth_token)
BASECAMP_REGISTRY_PASSWORD=$(kamal secrets extract BASECAMP_REGISTRY_PASSWORD $SECRETS)
+1 -1
View File
@@ -1,4 +1,4 @@
SECRETS=$(kamal secrets fetch --adapter 1password --account basecamp --from Deploy/Fizzy Deployments/BASECAMP_REGISTRY_PASSWORD Deployments/DASH_BASIC_AUTH_SECRET Production/RAILS_MASTER_KEY Production/MYSQL_ALTER_PASSWORD Production/MYSQL_ALTER_USER Production/MYSQL_APP_PASSWORD Production/MYSQL_APP_USER Production/MYSQL_READONLY_PASSWORD Production/MYSQL_READONLY_USER Production/SECRET_KEY_BASE Production/VAPID_PUBLIC_KEY Production/VAPID_PRIVATE_KEY Production/ACTIVE_STORAGE_ACCESS_KEY_ID Production/ACTIVE_STORAGE_SECRET_ACCESS_KEY Production/QUEENBEE_API_TOKEN Production/SIGNAL_ID_SECRET Production/SENTRY_DSN Production/ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY Production/ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY Production/ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT Production/STRIPE_MONTHLY_V1_PRICE_ID Production/STRIPE_MONTHLY_EXTRA_STORAGE_V1_PRICE_ID Production/STRIPE_SECRET_KEY Production/STRIPE_WEBHOOK_SECRET)
SECRETS=$(kamal secrets fetch --adapter 1password --account 23QPQDKZC5BKBIIG7UGT5GR5RM --from Deploy/Fizzy Deployments/BASECAMP_REGISTRY_PASSWORD Deployments/DASH_BASIC_AUTH_SECRET Production/RAILS_MASTER_KEY Production/MYSQL_ALTER_PASSWORD Production/MYSQL_ALTER_USER Production/MYSQL_APP_PASSWORD Production/MYSQL_APP_USER Production/MYSQL_READONLY_PASSWORD Production/MYSQL_READONLY_USER Production/SECRET_KEY_BASE Production/VAPID_PUBLIC_KEY Production/VAPID_PRIVATE_KEY Production/ACTIVE_STORAGE_ACCESS_KEY_ID Production/ACTIVE_STORAGE_SECRET_ACCESS_KEY Production/QUEENBEE_API_TOKEN Production/SIGNAL_ID_SECRET Production/SENTRY_DSN Production/ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY Production/ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY Production/ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT Production/STRIPE_MONTHLY_V1_PRICE_ID Production/STRIPE_MONTHLY_EXTRA_STORAGE_V1_PRICE_ID Production/STRIPE_SECRET_KEY Production/STRIPE_WEBHOOK_SECRET)
GITHUB_TOKEN=$(gh config get -h github.com oauth_token)
BASECAMP_REGISTRY_PASSWORD=$(kamal secrets extract BASECAMP_REGISTRY_PASSWORD $SECRETS)
+1 -1
View File
@@ -1,4 +1,4 @@
SECRETS=$(kamal secrets fetch --adapter 1password --account basecamp --from Deploy/Fizzy Deployments/BASECAMP_REGISTRY_PASSWORD Deployments/DASH_BASIC_AUTH_SECRET Staging/RAILS_MASTER_KEY Staging/MYSQL_ALTER_PASSWORD Staging/MYSQL_ALTER_USER Staging/MYSQL_APP_PASSWORD Staging/MYSQL_APP_USER Staging/MYSQL_READONLY_PASSWORD Staging/MYSQL_READONLY_USER Staging/SECRET_KEY_BASE Staging/VAPID_PUBLIC_KEY Staging/VAPID_PRIVATE_KEY Staging/ACTIVE_STORAGE_ACCESS_KEY_ID Staging/ACTIVE_STORAGE_SECRET_ACCESS_KEY Staging/QUEENBEE_API_TOKEN Staging/SIGNAL_ID_SECRET Staging/SENTRY_DSN Staging/ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY Staging/ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY Staging/ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT Staging/STRIPE_MONTHLY_V1_PRICE_ID Staging/STRIPE_MONTHLY_EXTRA_STORAGE_V1_PRICE_ID Staging/STRIPE_SECRET_KEY Staging/STRIPE_WEBHOOK_SECRET)
SECRETS=$(kamal secrets fetch --adapter 1password --account 23QPQDKZC5BKBIIG7UGT5GR5RM --from Deploy/Fizzy Deployments/BASECAMP_REGISTRY_PASSWORD Deployments/DASH_BASIC_AUTH_SECRET Staging/RAILS_MASTER_KEY Staging/MYSQL_ALTER_PASSWORD Staging/MYSQL_ALTER_USER Staging/MYSQL_APP_PASSWORD Staging/MYSQL_APP_USER Staging/MYSQL_READONLY_PASSWORD Staging/MYSQL_READONLY_USER Staging/SECRET_KEY_BASE Staging/VAPID_PUBLIC_KEY Staging/VAPID_PRIVATE_KEY Staging/ACTIVE_STORAGE_ACCESS_KEY_ID Staging/ACTIVE_STORAGE_SECRET_ACCESS_KEY Staging/QUEENBEE_API_TOKEN Staging/SIGNAL_ID_SECRET Staging/SENTRY_DSN Staging/ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY Staging/ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY Staging/ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT Staging/STRIPE_MONTHLY_V1_PRICE_ID Staging/STRIPE_MONTHLY_EXTRA_STORAGE_V1_PRICE_ID Staging/STRIPE_SECRET_KEY Staging/STRIPE_WEBHOOK_SECRET)
GITHUB_TOKEN=$(gh config get -h github.com oauth_token)
BASECAMP_REGISTRY_PASSWORD=$(kamal secrets extract BASECAMP_REGISTRY_PASSWORD $SECRETS)
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
url=$(op read "op://Deploy/Deploy Chatbot/url" --account Basecamp)
url=$(op read "op://Deploy/Deploy Chatbot/url" --account 23QPQDKZC5BKBIIG7UGT5GR5RM)
curl -s -d content="[Fizzy] ${1}" "${url}"
+1 -1
View File
@@ -27,7 +27,7 @@ end
# Fetch secrets from 1Password
secrets_escaped = `kamal secrets fetch \
--adapter 1password \
--account basecamp \
--account 23QPQDKZC5BKBIIG7UGT5GR5RM \
--from "Deploy/Fizzy" \
"Development/STRIPE_SECRET_KEY" \
"Development/STRIPE_MONTHLY_V1_PRICE_ID" \
+32
View File
@@ -51,6 +51,38 @@ class SsrfProtectionTest < ActiveSupport::TestCase
assert_equal "93.184.216.34", SsrfProtection.resolve_public_ip("multi.example.com")
end
# IPv6 address format tests (SSRF bypass prevention)
test "blocks IPv4-mapped IPv6 addresses with private IPs" do
stub_dns_resolution("::ffff:192.168.1.1")
assert_nil SsrfProtection.resolve_public_ip("mapped-private.example.com")
end
test "blocks IPv4-mapped IPv6 addresses with link-local IPs" do
stub_dns_resolution("::ffff:169.254.169.254")
assert_nil SsrfProtection.resolve_public_ip("mapped-metadata.example.com")
end
test "blocks IPv4-mapped IPv6 addresses even with public IPs" do
stub_dns_resolution("::ffff:93.184.216.34")
assert_nil SsrfProtection.resolve_public_ip("mapped-public.example.com")
end
test "blocks IPv4-compatible IPv6 addresses with private IPs" do
stub_dns_resolution("::192.168.1.1")
assert_nil SsrfProtection.resolve_public_ip("compat-private.example.com")
end
test "blocks IPv4-compatible IPv6 addresses with link-local IPs" do
stub_dns_resolution("::169.254.169.254")
assert_nil SsrfProtection.resolve_public_ip("compat-metadata.example.com")
end
test "blocks IPv4-compatible IPv6 addresses even with public IPs" do
stub_dns_resolution("::93.184.216.34")
assert_nil SsrfProtection.resolve_public_ip("compat-public.example.com")
end
private
def stub_dns_resolution(*ips)
dns_mock = mock("dns")