From eca8c6ce1f2391efe2294fcd255b35d2f6426fd3 Mon Sep 17 00:00:00 2001 From: arumaio <108123468+arumaio@users.noreply.github.com> Date: Sun, 24 May 2026 13:27:27 +0200 Subject: [PATCH 01/12] =?UTF-8?q?fix=20:=20account=20destroyed=20cascade?= =?UTF-8?q?=20transfer=20destruction=20then=20=E2=80=A6=20(#1795)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: cascade destroy transfers and reset transaction kind on account destruction. * Add rescue no method to transfer transaction reset --------- Co-authored-by: arumaio --- app/models/account.rb | 11 +++++++++++ app/models/transfer.rb | 12 ++++++++++-- test/models/account_test.rb | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/app/models/account.rb b/app/models/account.rb index b0595d308..6e11de9c4 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -2,7 +2,10 @@ class Account < ApplicationRecord include AASM, Syncable, Monetizable, Chartable, Linkable, Enrichable, Anchorable, Reconcileable, TaxTreatable before_validation :assign_default_owner, if: -> { owner_id.blank? } + before_destroy :capture_account_statement_ids_to_move + before_destroy :cleanup_transfers + after_destroy_commit :move_account_statements_to_inbox validates :name, :balance, :currency, presence: true @@ -543,4 +546,12 @@ class Account < ApplicationRecord updated_at: Time.current ) end + + def cleanup_transfers + transaction_ids = entries.where(entryable_type: "Transaction").pluck(:entryable_id) + + transfers = Transfer.where(inflow_transaction_id: transaction_ids).or(Transfer.where(outflow_transaction_id: transaction_ids)) + + transfers.find_each(&:destroy!) + end end diff --git a/app/models/transfer.rb b/app/models/transfer.rb index 878e899be..9b39d0f7d 100644 --- a/app/models/transfer.rb +++ b/app/models/transfer.rb @@ -38,8 +38,16 @@ class Transfer < ApplicationRecord # Once transfer is destroyed, we need to mark the denormalized kind fields on the transactions def destroy! Transfer.transaction do - inflow_transaction.update!(kind: "standard") - outflow_transaction.update!(kind: "standard") + [ inflow_transaction, outflow_transaction ].each do |transaction| + next if transaction.nil? + next unless Transaction.exists?(transaction.id) + begin + transaction.update!(kind: "standard") + rescue ActiveRecord::RecordNotFound + rescue NoMethodError + next + end + end super end end diff --git a/test/models/account_test.rb b/test/models/account_test.rb index 6b1507159..de44d90cb 100644 --- a/test/models/account_test.rb +++ b/test/models/account_test.rb @@ -382,4 +382,41 @@ class AccountTest < ActiveSupport::TestCase assert_equal [ provider_holding.id, second_provider_holding.id ].sort, account.current_holdings.pluck(:id).sort assert_equal %w[CHF EUR], account.current_holdings.pluck(:currency).sort end + + test "on account destroyed cascade transfer destroyed" do + outflow_account = @family.accounts.create!({ + owner: @admin, + name: "test_account_outflow", + balance: 100, + currency: "USD", + accountable_type: "Depository", + accountable_attributes: {} + }) + inflow_account = @family.accounts.create!({ + owner: @admin, + name: "test_account_inflow", + balance: 100, + currency: "USD", + accountable_type: "Depository", + accountable_attributes: {} + }) + + transfer = create_transfer( + from_account: outflow_account, + to_account: inflow_account, + amount: 50 + ) + + outflow_transaction = transfer.outflow_transaction + + outflow_transaction.reload + assert_equal "funds_movement", outflow_transaction.kind + + inflow_account.destroy! + + assert_raises(ActiveRecord::RecordNotFound) { transfer.reload } + + outflow_transaction.reload + assert_equal "standard", outflow_transaction.kind + end end From 98ca1608f4acdbfec3d07fc721e9661284793748 Mon Sep 17 00:00:00 2001 From: dripsmvcp <138900956+dripsmvcp@users.noreply.github.com> Date: Sun, 24 May 2026 20:43:36 +0900 Subject: [PATCH 02/12] fix(enable_banking): match bank list search against BIC, not just name (#1874) * fix(enable_banking): match bank list search against BIC, not just name Bank-search filter on the Enable Banking bank-selection modal only indexed `aspsp[:name]`, so users searching by BIC code (e.g. `INGDDEFF`) got no results even when the bank was rendered in the list. Switch the per-item data attribute to a `name + BIC` haystack and read from it in the Stimulus controller, so either token matches. Refs #1814 * style(bank_search): apply Biome formatting to forEach callback (#1874 review) --- .../controllers/bank_search_controller.js | 6 +-- .../enable_banking_items/select_bank.html.erb | 2 +- .../enable_banking_items_controller_test.rb | 42 +++++++++++++++++++ 3 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 test/controllers/enable_banking_items_controller_test.rb diff --git a/app/javascript/controllers/bank_search_controller.js b/app/javascript/controllers/bank_search_controller.js index a2c045cd9..155a7a8bb 100644 --- a/app/javascript/controllers/bank_search_controller.js +++ b/app/javascript/controllers/bank_search_controller.js @@ -7,9 +7,9 @@ export default class extends Controller { const query = this.inputTarget.value.toLocaleLowerCase().trim(); let visibleCount = 0; - this.itemTargets.forEach(item => { - const name = item.dataset.bankName?.toLocaleLowerCase() ?? ""; - const match = name.includes(query); + this.itemTargets.forEach((item) => { + const haystack = (item.dataset.bankSearch ?? "").toLocaleLowerCase(); + const match = haystack.includes(query); item.style.display = match ? "" : "none"; if (match) visibleCount++; }); diff --git a/app/views/enable_banking_items/select_bank.html.erb b/app/views/enable_banking_items/select_bank.html.erb index 124c8e3ec..095ae6738 100644 --- a/app/views/enable_banking_items/select_bank.html.erb +++ b/app/views/enable_banking_items/select_bank.html.erb @@ -28,7 +28,7 @@
<% @aspsps.each do |aspsp| %> -
+
"> <%= button_to authorize_enable_banking_item_path(@enable_banking_item), method: :post, params: { aspsp_name: aspsp[:name], new_connection: @new_connection }, diff --git a/test/controllers/enable_banking_items_controller_test.rb b/test/controllers/enable_banking_items_controller_test.rb new file mode 100644 index 000000000..06005bc9a --- /dev/null +++ b/test/controllers/enable_banking_items_controller_test.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require "test_helper" +require "openssl" + +class EnableBankingItemsControllerTest < ActionDispatch::IntegrationTest + setup do + sign_in users(:family_admin) + @family = families(:dylan_family) + @item = @family.enable_banking_items.create!( + name: "Test Connection", + country_code: "DE", + application_id: "test_app_id", + client_certificate: OpenSSL::PKey::RSA.new(2048).to_pem + ) + end + + test "select_bank exposes ASPSP BIC in the searchable data attribute" do + Provider::EnableBanking.any_instance.stubs(:get_aspsps).returns( + aspsps: [ + { + name: "ING-DiBa AG", + country: "DE", + bic: "INGDDEFF", + beta: false, + psu_types: [ "personal" ], + auth_methods: [ { approach: "REDIRECT" } ] + } + ] + ) + + get select_bank_enable_banking_item_url(@item) + + assert_response :success + haystack = @response.body[/data-bank-search="([^"]*)"/, 1] + assert haystack, "Expected list items to render a data-bank-search attribute the client filter reads from" + assert_includes haystack, "ingddeff", + "Expected the searchable data attribute to include the BIC so users can find banks by BIC code" + assert_includes haystack, "ing-diba ag", + "Expected the searchable data attribute to still include the bank name (existing name-search behavior)" + end +end From ca895416a4df0d44d2bccc04c19805930df7f082 Mon Sep 17 00:00:00 2001 From: Josh Date: Sun, 24 May 2026 07:50:44 -0400 Subject: [PATCH 03/12] chore(helm): bump pipelock to 2.5.0 and surface 2.5 config (#1913) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(helm): bump pipelock to 2.5.0 and surface 2.5 config Bumps pipelock.image.tag from 2.2.0 to 2.5.0 and exposes the most relevant 2.5 features as structured Helm values: - pipelock.requestBodyScanning: scan outbound bodies and sensitive headers for prompt-injection and DLP payloads. Disabled by default; roll out with action=warn before flipping to block. - pipelock.healthWatchdog: structured config for the wedge-detection watchdog with an exposeSubsystems toggle for /health detail. - pipelock.mcpToolPolicy.rules: structured values for rendering mcp_tool_policy.rules including redirect-profile references. Also fixes a latent config-validation regression: pipelock 2.x rejects an enabled mcp_tool_policy with no rules, but the chart previously defaulted to enabled=true with an empty rules list, which hard-fails 'pipelock check'. The default is now enabled=false; operators must explicitly enable and provide at least one rule. Refreshes README, CHANGELOG, docs/hosting/pipelock.md, docs/hosting/ai.md, compose example pin comment, and pipelock.example.yaml to call out 2.5 highlights (Audit Packet v0 verifiers, SPIFFE-strict envelopes, scanner attribution on MCP block receipts, pipelock doctor). Also fixes a stale docs/hosting/mcp.md reference to the removed compose.example.pipelock.yml. * chore(helm): fail helm template when mcp_tool_policy enabled with no rules Adds a guard in asserts.tpl so an operator who sets pipelock.mcpToolPolicy.enabled=true without populating pipelock.mcpToolPolicy.rules gets a clear render-time error instead of a container crash-loop with the pipelock validation message. Per CodeRabbit feedback on #1913. * Versions --------- Co-authored-by: Juan José Mata --- charts/sure/CHANGELOG.md | 16 ++++-- charts/sure/README.md | 42 +++++++++++++- charts/sure/templates/asserts.tpl | 15 +++++ charts/sure/templates/pipelock-configmap.yaml | 57 +++++++++++++++++++ charts/sure/values.yaml | 37 ++++++++++-- compose.example.ai.yml | 2 +- docs/hosting/ai.md | 9 ++- docs/hosting/mcp.md | 2 +- docs/hosting/pipelock.md | 6 +- pipelock.example.yaml | 32 +++++++++-- 10 files changed, 196 insertions(+), 22 deletions(-) diff --git a/charts/sure/CHANGELOG.md b/charts/sure/CHANGELOG.md index 0abee238d..490cbe136 100644 --- a/charts/sure/CHANGELOG.md +++ b/charts/sure/CHANGELOG.md @@ -5,16 +5,22 @@ All notable changes to the Sure Helm chart will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [0.7.1] - 2026-05-31] ### Changed -- Bumped `pipelock.image.tag` from `2.0.0` to `2.2.0` (three minor releases behind latest). Floating `@v2` CI action pin picks up patch/minor updates automatically. -- Refreshed pipelock feature notes in the chart README, `docs/hosting/pipelock.md`, and `pipelock.example.yaml` to reference the upstream changelog instead of a single version. +- Bumped `pipelock.image.tag` from `2.2.0` to `2.5.0`. Picks up three releases of scanner, federation, and audit work — see the [pipelock changelog](https://github.com/luckyPipewrench/pipelock/blob/main/CHANGELOG.md) for the full surface. +- Refreshed pipelock feature notes in the chart README, `docs/hosting/pipelock.md`, and `pipelock.example.yaml` to call out 2.5 highlights (Audit Packet v0, request-body prompt-injection blocking, SPIFFE-strict inbound envelopes, scanner attribution on MCP block receipts). +- Expanded the `pipelock.extraConfig` escape-hatch comment to reference the new sections available in 2.5 (browser_shield, mediation_envelope, redaction, learn, media_policy, a2a_scanning, emit). +- Defaulted `pipelock.mcpToolPolicy.enabled` to `false`. Pipelock 2.x rejects an enabled `mcp_tool_policy` with no rules, so the prior chart default (`enabled: true`, empty rules) hard-failed config validation on startup. Operators who want tool policy active must now set `enabled: true` and provide at least one entry in `pipelock.mcpToolPolicy.rules`. ### Added +- `pipelock.requestBodyScanning` (pipelock 2.5+): structured config for scanning outbound request bodies for prompt-injection and bodies/sensitive headers for DLP payloads. Disabled by default to preserve existing chart behavior; opt in with `enabled: true`. +- `pipelock.healthWatchdog` (pipelock 2.4+): structured config for the wedge-detection watchdog. Operators can opt into per-subsystem detail in `/health` responses via `exposeSubsystems: true`. +- `pipelock.mcpToolPolicy.rules`: structured Helm values for rendering `mcp_tool_policy.rules`, including redirect-profile references. +- `asserts.tpl` guard that fails `helm template`/`helm install` when `pipelock.mcpToolPolicy.enabled=true` is paired with an empty `rules` list, surfacing the pipelock validation error at render time instead of container startup. - README: CI scan status badge for the pipelock workflow. -## [0.6.9-alpha] - 2026-03-24 +## [0.6.9] - 2026-03-31 ### Changed - Bumped `pipelock.image.tag` from `1.5.0` to `2.0.0` @@ -58,7 +64,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Renamed `_asserts.tpl` to `asserts.tpl` — Helm's `_` prefix convention prevented guards from executing -## [0.6.7-alpha] - 2026-01-10 +## [0.6.7] - 2026-01-31 ### Added - **Redis Sentinel support for Sidekiq high availability**: Application now automatically detects and configures Sidekiq to use Redis Sentinel when `redisOperator.mode=sentinel` and `redisOperator.sentinel.enabled=true` diff --git a/charts/sure/README.md b/charts/sure/README.md index 73d5eb464..42a6688c8 100644 --- a/charts/sure/README.md +++ b/charts/sure/README.md @@ -645,7 +645,7 @@ hpa: - **Forward proxy** (port 8888): Scans outbound HTTPS from Faraday-based AI clients. Auto-injected via `HTTPS_PROXY` env vars when enabled. - **MCP reverse proxy** (port 8889): Scans inbound MCP traffic from external AI assistants. -Recent pipelock releases add enhanced tool poisoning detection (full JSON schema scanning), per-read kill switch preemption, trusted domain allowlisting, MCP tool redirect profiles, signed action receipts, per-pattern DLP warn mode, and the `pipelock posture verify` / `pipelock session` CLIs. Process sandboxing and attack simulation are also available via `extraConfig` and CLI. See the [pipelock changelog](https://github.com/luckyPipewrench/pipelock/releases) for details. +Recent pipelock releases add the Audit Packet v0 schema and language-portable verifiers (Go, TypeScript, Rust), request-body prompt-injection blocking, SPIFFE-strict inbound mediation envelopes, scanner attribution on MCP block receipts, enhanced tool poisoning detection, per-read kill switch preemption, trusted domain allowlisting, MCP tool redirect profiles, signed action receipts, per-pattern DLP warn mode, learn-and-lock behavioural contracts, the wedge-detection health watchdog, and the `pipelock posture verify` / `pipelock session` / `pipelock doctor` CLIs. Browser Shield, process sandboxing, and attack simulation are available via `extraConfig` and CLI. See the [pipelock changelog](https://github.com/luckyPipewrench/pipelock/releases) for details. ### Enabling Pipelock @@ -653,7 +653,7 @@ Recent pipelock releases add enhanced tool poisoning detection (full JSON schema pipelock: enabled: true image: - tag: "2.2.0" + tag: "2.5.0" mode: balanced # strict, balanced, or audit ``` @@ -677,12 +677,45 @@ pipelock: mcpToolPolicy: enabled: true action: redirect # or use per-rule action overrides + rules: + - name: redirect-fetch + toolPattern: "^(fetch|web_fetch)$" + action: redirect + redirectProfile: safe-fetch redirectProfiles: safe-fetch: exec: ["/pipelock", "internal-redirect", "fetch-proxy"] reason: "Route fetch calls through audited proxy" ``` +### Request body scanning (pipelock 2.5+) + +Pipelock 2.5 added prompt-injection detection on outbound request bodies (JSON, form-encoded, raw text, WebSocket frames). When enabled, findings hard-block non-provider destinations even when `action: warn`; trusted provider hosts (OpenAI, Anthropic, etc.) remain exempt through the response-scanning exemption list. + +```yaml +pipelock: + requestBodyScanning: + enabled: true + action: warn # warn or block + maxBodyBytes: 5242880 # 5 MB; fail-closed above this + scanHeaders: true + headerMode: sensitive # "sensitive" or "all" +``` + +Disabled by default. Roll out with `action: warn` first to observe findings in logs without blocking, then flip to `action: block` once the false-positive rate is acceptable. + +### Health watchdog + +The wedge-detection watchdog returns 503 on `/health` when a subsystem heartbeat (proxy hot path, MCP listener, rules-engine reload watcher) goes stale. Enabled by default in pipelock; the chart exposes the controls so operators can opt into per-subsystem detail in the health payload: + +```yaml +pipelock: + healthWatchdog: + enabled: true + intervalSeconds: 2 + exposeSubsystems: true # adds per-subsystem boolean map to /health +``` + ### Validating your config Pipelock includes CLI tools for config validation: @@ -693,6 +726,9 @@ pipelock simulate --config pipelock.yaml # Score your config's security posture (0-100) pipelock audit score --config pipelock.yaml + +# Report whether configured protections are actually enforceable +pipelock doctor ``` ### Exposing MCP to external AI assistants @@ -811,7 +847,7 @@ See `values.yaml` for the complete configuration surface, including: - `migrations.*`: strategy job or initContainer - `simplefin.encryption.*`: enable + backfill options - `cronjobs.*`: custom CronJobs -- `pipelock.*`: AI agent security proxy (forward proxy, MCP reverse proxy, DLP, injection scanning, trusted domains, tool redirect profiles, logging, serviceMonitor, ingress, PDB, extraConfig) +- `pipelock.*`: AI agent security proxy (forward proxy, MCP reverse proxy, DLP, injection scanning, request-body scanning, health watchdog, trusted domains, tool redirect profiles, logging, serviceMonitor, ingress, PDB, extraConfig) - `service.*`, `ingress.*`, `serviceMonitor.*`, `hpa.*` ## Helm tests diff --git a/charts/sure/templates/asserts.tpl b/charts/sure/templates/asserts.tpl index 1d481c0e9..52b3af9ac 100644 --- a/charts/sure/templates/asserts.tpl +++ b/charts/sure/templates/asserts.tpl @@ -21,3 +21,18 @@ Mutual exclusivity and configuration guards {{- if and $extEnabled (not $plEnabled) $requirePL -}} {{- fail "pipelock.requireForExternalAssistant is true but pipelock.enabled is false. Enable pipelock (pipelock.enabled=true) when using rails.externalAssistant, or set pipelock.requireForExternalAssistant=false." -}} {{- end -}} + +{{/* +Pipelock 2.x rejects an enabled mcp_tool_policy with no rules; surface this +at helm template time instead of waiting for the container to crash-loop. +*/}} +{{- if $plEnabled -}} +{{- $mtp := .Values.pipelock.mcpToolPolicy | default (dict) -}} +{{- if hasKey $mtp "enabled" -}} +{{- if $mtp.enabled -}} +{{- if eq (len ($mtp.rules | default (list))) 0 -}} +{{- fail "pipelock.mcpToolPolicy.enabled=true requires at least one entry in pipelock.mcpToolPolicy.rules. Pipelock rejects an enabled tool policy with no rules." -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/sure/templates/pipelock-configmap.yaml b/charts/sure/templates/pipelock-configmap.yaml index b9c8fa5b7..c77ee68ba 100644 --- a/charts/sure/templates/pipelock-configmap.yaml +++ b/charts/sure/templates/pipelock-configmap.yaml @@ -65,6 +65,34 @@ {{- $chainWindow = int (.Values.pipelock.toolChainDetection.windowSize | default 20) -}} {{- $chainGap = int (.Values.pipelock.toolChainDetection.maxGap | default 3) -}} {{- end -}} +{{- $rbsEnabled := false -}} +{{- $rbsAction := "warn" -}} +{{- $rbsMaxBytes := 5242880 -}} +{{- $rbsScanHeaders := true -}} +{{- $rbsHeaderMode := "sensitive" -}} +{{- if .Values.pipelock.requestBodyScanning -}} +{{- if hasKey .Values.pipelock.requestBodyScanning "enabled" -}} +{{- $rbsEnabled = .Values.pipelock.requestBodyScanning.enabled -}} +{{- end -}} +{{- $rbsAction = .Values.pipelock.requestBodyScanning.action | default "warn" -}} +{{- $rbsMaxBytes = int (.Values.pipelock.requestBodyScanning.maxBodyBytes | default 5242880) -}} +{{- if hasKey .Values.pipelock.requestBodyScanning "scanHeaders" -}} +{{- $rbsScanHeaders = .Values.pipelock.requestBodyScanning.scanHeaders -}} +{{- end -}} +{{- $rbsHeaderMode = .Values.pipelock.requestBodyScanning.headerMode | default "sensitive" -}} +{{- end -}} +{{- $hwEnabled := true -}} +{{- $hwInterval := 2 -}} +{{- $hwExpose := false -}} +{{- if .Values.pipelock.healthWatchdog -}} +{{- if hasKey .Values.pipelock.healthWatchdog "enabled" -}} +{{- $hwEnabled = .Values.pipelock.healthWatchdog.enabled -}} +{{- end -}} +{{- $hwInterval = int (.Values.pipelock.healthWatchdog.intervalSeconds | default 2) -}} +{{- if hasKey .Values.pipelock.healthWatchdog "exposeSubsystems" -}} +{{- $hwExpose = .Values.pipelock.healthWatchdog.exposeSubsystems -}} +{{- end -}} +{{- end -}} {{- $logFormat := "json" -}} {{- $logOutput := "stdout" -}} {{- $logIncludeAllowed := false -}} @@ -128,6 +156,25 @@ data: mcp_tool_policy: enabled: {{ $mcpPolicyEnabled }} action: {{ $mcpPolicyAction }} +{{- if and .Values.pipelock.mcpToolPolicy .Values.pipelock.mcpToolPolicy.rules }} + rules: +{{- range .Values.pipelock.mcpToolPolicy.rules }} + - name: {{ required "pipelock.mcpToolPolicy.rules[].name is required" .name | quote }} + tool_pattern: {{ required "pipelock.mcpToolPolicy.rules[].toolPattern is required" .toolPattern | quote }} +{{- if .argPattern }} + arg_pattern: {{ .argPattern | quote }} +{{- end }} +{{- if .argKey }} + arg_key: {{ .argKey | quote }} +{{- end }} +{{- if .action }} + action: {{ .action | quote }} +{{- end }} +{{- if .redirectProfile }} + redirect_profile: {{ .redirectProfile | quote }} +{{- end }} +{{- end }} +{{- end }} {{- if and .Values.pipelock.mcpToolPolicy .Values.pipelock.mcpToolPolicy.redirectProfiles }} redirect_profiles: {{- toYaml .Values.pipelock.mcpToolPolicy.redirectProfiles | nindent 8 }} @@ -140,6 +187,16 @@ data: action: {{ $chainAction }} window_size: {{ $chainWindow }} max_gap: {{ $chainGap }} + request_body_scanning: + enabled: {{ $rbsEnabled }} + action: {{ $rbsAction }} + max_body_bytes: {{ $rbsMaxBytes }} + scan_headers: {{ $rbsScanHeaders }} + header_mode: {{ $rbsHeaderMode }} + health_watchdog: + enabled: {{ $hwEnabled }} + interval_seconds: {{ $hwInterval }} + expose_subsystems: {{ $hwExpose }} logging: format: {{ $logFormat }} output: {{ $logOutput }} diff --git a/charts/sure/values.yaml b/charts/sure/values.yaml index f4b54f69e..d01d0dc5e 100644 --- a/charts/sure/values.yaml +++ b/charts/sure/values.yaml @@ -497,7 +497,7 @@ pipelock: enabled: false image: repository: ghcr.io/luckypipewrench/pipelock - tag: "2.2.0" + tag: "2.5.0" pullPolicy: IfNotPresent imagePullSecrets: [] replicas: 1 @@ -530,8 +530,17 @@ pipelock: originPolicy: rewrite # rewrite, forward, or strip # MCP tool policy: pre-execution rules for tool calls (shell obfuscation, etc.) mcpToolPolicy: - enabled: true + # Enable only when at least one rule is configured; pipelock rejects an + # enabled mcp_tool_policy with no rules. + enabled: false action: warn + # Example: + # rules: + # - name: redirect-fetch + # toolPattern: "^(fetch|web_fetch)$" + # action: redirect + # redirectProfile: safe-fetch + rules: [] # Redirect profiles: route matched tool calls to audited handler programs instead # of blocking. The handler returns a synthetic MCP response. Fail-closed on error. # Example: @@ -550,6 +559,24 @@ pipelock: action: warn windowSize: 20 maxGap: 3 + # Request body scanning (pipelock 2.5+): detect prompt-injection payloads + # in outbound request bodies (JSON, form-encoded, raw text, WebSocket frames). + # In enforce mode, prompt-injection findings hard-block non-provider + # destinations even when action is "warn"; trusted provider hosts (OpenAI, + # Anthropic, etc.) remain exempt via the response_scanning exemption list. + requestBodyScanning: + enabled: false + action: warn # warn or block + maxBodyBytes: 5242880 # 5 MB; fail-closed above this + scanHeaders: true # scan request headers for DLP + headerMode: sensitive # "sensitive" or "all" + # Health watchdog (pipelock 2.4+): /health returns 503 when any subsystem + # heartbeat goes stale. Enabled by default in pipelock; exposed here so + # operators can opt into per-subsystem detail in the health payload. + healthWatchdog: + enabled: true + intervalSeconds: 2 + exposeSubsystems: false # include per-subsystem map in /health response service: type: ClusterIP resources: @@ -601,8 +628,10 @@ pipelock: includeBlocked: true # Escape hatch: ADDITIONAL config sections appended to pipelock.yaml. - # Use for sections not covered by structured values above (session_profiling, - # data_budget, adaptive_enforcement, kill_switch, sandbox, reverse_proxy, etc.) + # Use for sections not covered by structured values above. Examples: + # session_profiling, data_budget, adaptive_enforcement, kill_switch, sandbox, + # reverse_proxy, redaction, browser_shield, mediation_envelope, learn, + # media_policy, a2a_scanning, emit (OTel agent.threat.detection attributes). # Do NOT duplicate keys already rendered above - behavior is parser-dependent. extraConfig: {} diff --git a/compose.example.ai.yml b/compose.example.ai.yml index 7c79178f7..48a696f38 100644 --- a/compose.example.ai.yml +++ b/compose.example.ai.yml @@ -112,7 +112,7 @@ x-rails-env: &rails_env services: pipelock: - image: ghcr.io/luckypipewrench/pipelock:latest # pin to a specific version (e.g., :2.2.0) for production + image: ghcr.io/luckypipewrench/pipelock:latest # pin to a specific version (e.g., :2.5.0) for production container_name: pipelock hostname: pipelock restart: unless-stopped diff --git a/docs/hosting/ai.md b/docs/hosting/ai.md index f13f79046..70c89d557 100644 --- a/docs/hosting/ai.md +++ b/docs/hosting/ai.md @@ -450,13 +450,18 @@ Pipelock scans for prompt injection, DLP violations, and tool poisoning. The ext **`NO_PROXY` behavior (Helm/Kubernetes only):** The Helm chart's env template sets `NO_PROXY` to include `.svc.cluster.local` and other internal domains. This means in-cluster agent URLs (like `http://agent.namespace.svc.cluster.local:18789`) bypass the forward proxy and go directly. If your agent is in-cluster, its traffic won't be forward-proxy scanned (but MCP callbacks from the agent are still scanned by the reverse proxy). Docker Compose deployments use a different `NO_PROXY` set; check your compose file for the exact values. -**`mcpToolPolicy` note:** The Helm chart's `pipelock.mcpToolPolicy.enabled` defaults to `true`. If you haven't defined any policy rules, disable it: +**`mcpToolPolicy` note:** The Helm chart's `pipelock.mcpToolPolicy.enabled` defaults to `false`. Pipelock rejects an enabled tool policy with no rules, so the chart ships it off by default. To turn it on, define at least one rule and set `enabled: true`: ```yaml # Helm values pipelock: mcpToolPolicy: - enabled: false + enabled: true + action: warn + rules: + - name: example + toolPattern: "^shell$" + action: block ``` See the [Pipelock documentation](https://github.com/luckyPipewrench/pipelock) for tool policy configuration details. diff --git a/docs/hosting/mcp.md b/docs/hosting/mcp.md index 671feb700..db019163f 100644 --- a/docs/hosting/mcp.md +++ b/docs/hosting/mcp.md @@ -231,7 +231,7 @@ Pipelock provides: - **Tool poisoning detection**: Prevents malicious tool call sequences - **Policy enforcement**: Block or warn on suspicious patterns -See the [Pipelock documentation](pipelock.md) and the example configuration in `compose.example.pipelock.yml` for setup instructions. +See the [Pipelock documentation](pipelock.md) and the example configuration in `compose.example.ai.yml` for setup instructions. ### Network Security diff --git a/docs/hosting/pipelock.md b/docs/hosting/pipelock.md index 08c14fb13..0cd5ed126 100644 --- a/docs/hosting/pipelock.md +++ b/docs/hosting/pipelock.md @@ -77,13 +77,13 @@ Enable Pipelock in your Helm values: pipelock: enabled: true image: - tag: "2.2.0" + tag: "2.5.0" mode: balanced ``` This creates a separate Deployment, Service, and ConfigMap. The chart auto-injects `HTTPS_PROXY`/`HTTP_PROXY`/`NO_PROXY` into web and worker pods. -Recent pipelock releases add trusted domain allowlisting, MCP tool redirect profiles, enhanced tool poisoning detection (full JSON schema scanning), per-read kill switch preemption, signed action receipts, per-pattern DLP warn mode, and the `pipelock posture verify` / `pipelock session` CLI commands. See the [pipelock changelog](https://github.com/luckyPipewrench/pipelock/releases) for details. +Recent pipelock releases add the Audit Packet v0 schema and language-portable verifiers (Go/TypeScript/Rust), request-body prompt-injection blocking, SPIFFE-strict inbound mediation envelopes, scanner attribution on MCP block receipts, trusted domain allowlisting, MCP tool redirect profiles, enhanced tool poisoning detection, per-read kill switch preemption, signed action receipts, per-pattern DLP warn mode, learn-and-lock behavioural contracts, the wedge-detection health watchdog, and the `pipelock posture verify` / `pipelock session` / `pipelock doctor` CLI commands. See the [pipelock changelog](https://github.com/luckyPipewrench/pipelock/releases) for details. ### Exposing MCP to external agents (Kubernetes) @@ -149,6 +149,7 @@ The `pipelock.example.yaml` file (Docker Compose) or ConfigMap (Helm) controls s | `trusted_domains` | Allow internal services whose public DNS resolves to private IPs | | `forward_proxy` | Outbound HTTPS scanning (tunnel timeouts, idle timeouts) | | `dlp` | Data loss prevention (scan env vars, built-in patterns) | +| `request_body_scanning` | Scan outbound request bodies for prompt-injection and bodies/sensitive headers for DLP (pipelock 2.5+) | | `response_scanning` | Scan LLM responses for prompt injection | | `mcp_input_scanning` | Scan inbound MCP requests | | `mcp_tool_scanning` | Validate tool calls, detect drift | @@ -156,6 +157,7 @@ The `pipelock.example.yaml` file (Docker Compose) or ConfigMap (Helm) controls s | `mcp_session_binding` | Pin tool inventory, detect manipulation | | `tool_chain_detection` | Multi-step attack patterns | | `websocket_proxy` | WebSocket frame scanning (disabled by default) | +| `health_watchdog` | Wedge-detection on subsystem heartbeats, returns 503 on stall (pipelock 2.4+) | | `logging` | Output format (json/text), verbosity | For the Helm chart, most sections are configurable via `values.yaml`. For additional sections not covered by structured values (session profiling, data budgets, kill switch, sandbox, reverse proxy, adaptive enforcement), use the `extraConfig` escape hatch: diff --git a/pipelock.example.yaml b/pipelock.example.yaml index ee77d162f..4e2af49f9 100644 --- a/pipelock.example.yaml +++ b/pipelock.example.yaml @@ -1,12 +1,16 @@ # Pipelock configuration for Docker Compose # See https://github.com/luckyPipewrench/pipelock for full options. # -# Recent additions: trusted_domains, redirect profiles, attack simulation, -# security scoring, process sandbox, enhanced tool poisoning detection, signed -# action receipts, per-pattern DLP warn mode, and the `pipelock posture verify` -# / `pipelock session` CLIs. +# Recent additions (2.5): Audit Packet v0 schema with Go/TypeScript/Rust +# verifiers, request-body prompt-injection blocking, SPIFFE-strict inbound +# mediation envelopes, scanner attribution on MCP block receipts, wedge- +# detection health watchdog, learn-and-lock behavioural contracts, trusted +# domains, redirect profiles, attack simulation, security scoring, process +# sandbox, signed action receipts, per-pattern DLP warn mode, and the +# `pipelock posture verify` / `pipelock session` / `pipelock doctor` CLIs. # Run `pipelock simulate --config ` to test your config against 24 attack scenarios. # Run `pipelock audit score --config ` for a security posture score (0-100). +# Run `pipelock doctor` to verify configured protections are actually enforceable. version: 1 mode: balanced @@ -72,3 +76,23 @@ tool_chain_detection: action: warn window_size: 20 max_gap: 3 + +# Request body scanning (pipelock 2.5+): detect prompt-injection payloads in +# outbound request bodies (JSON, form-encoded, raw text, WebSocket frames). +# In enforce mode, prompt-injection findings hard-block non-provider +# destinations even when action is "warn". Trusted provider hosts (OpenAI, +# Anthropic, etc.) remain exempt via the response_scanning exemption list. +request_body_scanning: + enabled: false + action: warn + max_body_bytes: 5242880 + scan_headers: true + header_mode: sensitive + +# Health watchdog (pipelock 2.4+): /health returns 503 when any subsystem +# heartbeat goes stale. Enabled by default; set expose_subsystems true to +# include a per-subsystem boolean map in /health responses. +health_watchdog: + enabled: true + interval_seconds: 2 + expose_subsystems: false From 0988e2d9d653899207caf853f96f7d2903a137bf Mon Sep 17 00:00:00 2001 From: Abhinav Dhiman <8640877+ahnv@users.noreply.github.com> Date: Sun, 24 May 2026 17:32:50 +0530 Subject: [PATCH 04/12] perf: use jemalloc as the default allocator (#1910) * feat(docker): add jemalloc to reduce memory fragmentation Install libjemalloc2 in the base image and preload it via LD_PRELOAD in docker-entrypoint when available. Reduces RSS growth from glibc's default allocator fragmentation under Rails workloads. * feat(docker): add DISABLE_JEMALLOC env var + preserve existing LD_PRELOAD * feat(docker): add jemalloc status logging to entrypoint * refactor(docker): simplify jemalloc logging to warn-only when disabled/missing --- Dockerfile | 4 ++-- bin/docker-entrypoint | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2cb2c83e7..c165b18f7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,7 @@ WORKDIR /rails # Install base packages RUN apt-get update -qq \ - && apt-get install --no-install-recommends -y curl libvips postgresql-client libyaml-0-2 procps \ + && apt-get install --no-install-recommends -y curl libvips postgresql-client libyaml-0-2 procps libjemalloc2 \ && rm -rf /var/lib/apt/lists /var/cache/apt/archives # Set production environment @@ -19,7 +19,7 @@ ENV RAILS_ENV="production" \ BUNDLE_PATH="/usr/local/bundle" \ BUNDLE_WITHOUT="development" \ BUILD_COMMIT_SHA=${BUILD_COMMIT_SHA} - + # Throw-away build stage to reduce size of final image FROM base AS build diff --git a/bin/docker-entrypoint b/bin/docker-entrypoint index 67ef49314..672521a98 100755 --- a/bin/docker-entrypoint +++ b/bin/docker-entrypoint @@ -1,5 +1,15 @@ #!/bin/bash -e +# Use jemalloc to reduce memory fragmentation if available +JEMALLOC="/usr/lib/$(uname -m)-linux-gnu/libjemalloc.so.2" +if [ -f "$JEMALLOC" ] && [ -z "${DISABLE_JEMALLOC}" ]; then + export LD_PRELOAD="${LD_PRELOAD:+$LD_PRELOAD:}$JEMALLOC" +else + [ -n "${DISABLE_JEMALLOC}" ] \ + && echo "WARNING: jemalloc disabled via DISABLE_JEMALLOC" \ + || echo "WARNING: jemalloc not found at $JEMALLOC, skipping" +fi + # If running the rails server then create or migrate existing database if [ "${1}" == "./bin/rails" ] && [ "${2}" == "server" ]; then ./bin/rails db:prepare From c7c63a50a7df212aac10eb08c68fcc16d6fd20c2 Mon Sep 17 00:00:00 2001 From: "Sure Admin (bot)" Date: Sun, 24 May 2026 14:55:08 +0200 Subject: [PATCH 05/12] Add PR workflow for not-gittensor labeling (#1957) --- .github/workflows/label-not-gittensor.yml | 60 +++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 .github/workflows/label-not-gittensor.yml diff --git a/.github/workflows/label-not-gittensor.yml b/.github/workflows/label-not-gittensor.yml new file mode 100644 index 000000000..bfd74f822 --- /dev/null +++ b/.github/workflows/label-not-gittensor.yml @@ -0,0 +1,60 @@ +name: Label non-Gittensor PRs + +on: + pull_request_target: + types: + - opened + - reopened + +permissions: + pull-requests: write + +jobs: + label-pr: + runs-on: ubuntu-latest + steps: + - name: Add not-gittensor label for matched authors + uses: actions/github-script@v7 + env: + GITTENSOR_USERS: ${{ vars.GITTENSOR_USERS || '[]' }} + GITTENSOR_EXCEPTIONS: ${{ vars.GITTENSOR_EXCEPTIONS || '[]' }} + TARGET_LABEL: not-gittensor + with: + script: | + const parseList = (raw, name) => { + try { + const parsed = JSON.parse(raw || '[]'); + if (!Array.isArray(parsed)) { + core.setFailed(`${name} must be a JSON array.`); + return []; + } + return parsed.map((value) => String(value).toLowerCase()); + } catch (error) { + core.setFailed(`Failed to parse ${name}: ${error.message}`); + return []; + } + }; + + const author = context.payload.pull_request.user.login.toLowerCase(); + const users = new Set(parseList(process.env.GITTENSOR_USERS, 'GITTENSOR_USERS')); + const exceptions = new Set(parseList(process.env.GITTENSOR_EXCEPTIONS, 'GITTENSOR_EXCEPTIONS')); + + if (!users.has(author) || exceptions.has(author)) { + core.info(`No label needed for @${author}.`); + return; + } + + const existingLabels = context.payload.pull_request.labels.map((label) => label.name); + if (existingLabels.includes(process.env.TARGET_LABEL)) { + core.info(`Label ${process.env.TARGET_LABEL} already present.`); + return; + } + + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + labels: [process.env.TARGET_LABEL], + }); + + core.info(`Added ${process.env.TARGET_LABEL} to PR #${context.payload.pull_request.number}.`); From c93193cfbcea2788b3c16b849fa8f8d421620526 Mon Sep 17 00:00:00 2001 From: "sentry[bot]" <39604003+sentry[bot]@users.noreply.github.com> Date: Sun, 24 May 2026 15:13:49 +0200 Subject: [PATCH 06/12] fix(locale): Handle blank locale submission gracefully (#1876) Co-authored-by: sentry[bot] <39604003+sentry[bot]@users.noreply.github.com> --- app/controllers/users_controller.rb | 32 +++++++++++++++++------------ app/models/user.rb | 1 + 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index d265734c6..ab88dcc15 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -27,21 +27,27 @@ class UsersController < ApplicationController end else was_ai_enabled = @user.ai_enabled - @user.update!(user_params.except(:redirect_to, :delete_profile_image)) - @user.profile_image.purge if should_purge_profile_image? + if @user.update(user_params.except(:redirect_to, :delete_profile_image)) + @user.profile_image.purge if should_purge_profile_image? - # Add a special notice if AI was just enabled or disabled - notice = if !was_ai_enabled && @user.ai_enabled - "AI Assistant has been enabled successfully." - elsif was_ai_enabled && !@user.ai_enabled - "AI Assistant has been disabled." + # Add a special notice if AI was just enabled or disabled + notice = if !was_ai_enabled && @user.ai_enabled + "AI Assistant has been enabled successfully." + elsif was_ai_enabled && !@user.ai_enabled + "AI Assistant has been disabled." + else + t(".success") + end + + respond_to do |format| + format.html { handle_redirect(notice) } + format.json { head :ok } + end else - t(".success") - end - - respond_to do |format| - format.html { handle_redirect(notice) } - format.json { head :ok } + respond_to do |format| + format.html { redirect_to settings_profile_path, alert: @user.errors.full_messages.to_sentence } + format.json { render json: { errors: @user.errors.full_messages }, status: :unprocessable_entity } + end end end end diff --git a/app/models/user.rb b/app/models/user.rb index b1c40bb76..74016d755 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -51,6 +51,7 @@ class User < ApplicationRecord validates :password, length: { minimum: 8 }, allow_nil: true normalizes :email, with: ->(email) { email.strip.downcase } normalizes :unconfirmed_email, with: ->(email) { email&.strip&.downcase } + normalizes :locale, with: ->(locale) { locale.presence } normalizes :first_name, :last_name, with: ->(value) { value.strip.presence } From 5520bacbb87e565ffa2e434862df8f6e7ea7781d Mon Sep 17 00:00:00 2001 From: "sentry[bot]" <39604003+sentry[bot]@users.noreply.github.com> Date: Sun, 24 May 2026 15:37:13 +0200 Subject: [PATCH 07/12] fix(i18n): standardize product name interpolation in import mapping descriptions (#1956) Co-authored-by: sentry[bot] <39604003+sentry[bot]@users.noreply.github.com> --- app/views/import/confirms/show.html.erb | 2 +- config/locales/views/imports/de.yml | 8 ++++---- config/locales/views/imports/en.yml | 8 ++++---- config/locales/views/imports/es.yml | 8 ++++---- config/locales/views/imports/hu.yml | 8 ++++---- config/locales/views/imports/nb.yml | 8 ++++---- config/locales/views/imports/nl.yml | 8 ++++---- config/locales/views/imports/pl.yml | 8 ++++---- config/locales/views/imports/pt-BR.yml | 8 ++++---- config/locales/views/imports/ro.yml | 8 ++++---- config/locales/views/imports/tr.yml | 8 ++++---- config/locales/views/imports/zh-CN.yml | 8 ++++---- config/locales/views/imports/zh-TW.yml | 8 ++++---- 13 files changed, 49 insertions(+), 49 deletions(-) diff --git a/app/views/import/confirms/show.html.erb b/app/views/import/confirms/show.html.erb index 8560b761d..43afe9e70 100644 --- a/app/views/import/confirms/show.html.erb +++ b/app/views/import/confirms/show.html.erb @@ -67,7 +67,7 @@ <%= t(".#{step_mapping_class.name.demodulize.underscore}_title", import_type: @import.type.underscore.humanize) %>

- <%= t(".#{step_mapping_class.name.demodulize.underscore}_description", import_type: @import.type.underscore.humanize, product: product_name) %> + <%= t(".#{step_mapping_class.name.demodulize.underscore}_description", import_type: @import.type.underscore.humanize, product_name: product_name) %>

diff --git a/config/locales/views/imports/de.yml b/config/locales/views/imports/de.yml index bd733bc77..faab67cc0 100644 --- a/config/locales/views/imports/de.yml +++ b/config/locales/views/imports/de.yml @@ -61,13 +61,13 @@ de: rows_label: Zeilen unassigned_account: Neues Konto für nicht zugewiesene Zeilen erstellen? show: - account_mapping_description: Weise alle Konten aus deiner importierten Datei den bestehenden Konten in %{product} zu. Du kannst auch neue Konten hinzufügen oder sie ohne Kategorie lassen. + account_mapping_description: Weise alle Konten aus deiner importierten Datei den bestehenden Konten in %{product_name} zu. Du kannst auch neue Konten hinzufügen oder sie ohne Kategorie lassen. account_mapping_title: Konten zuweisen - account_type_mapping_description: Weise alle Kontotypen aus deiner importierten Datei den Kontotypen in %{product} zu. + account_type_mapping_description: Weise alle Kontotypen aus deiner importierten Datei den Kontotypen in %{product_name} zu. account_type_mapping_title: Kontotypen zuweisen - category_mapping_description: Weise alle Kategorien aus deiner importierten Datei den bestehenden Kategorien in %{product} zu. Du kannst auch neue Kategorien hinzufügen oder sie ohne Kategorie lassen. + category_mapping_description: Weise alle Kategorien aus deiner importierten Datei den bestehenden Kategorien in %{product_name} zu. Du kannst auch neue Kategorien hinzufügen oder sie ohne Kategorie lassen. category_mapping_title: Kategorien zuweisen - tag_mapping_description: Weise alle Tags aus deiner importierten Datei den bestehenden Tags in %{product} zu. Du kannst auch neue Tags hinzufügen oder sie ohne Kategorie lassen. + tag_mapping_description: Weise alle Tags aus deiner importierten Datei den bestehenden Tags in %{product_name} zu. Du kannst auch neue Tags hinzufügen oder sie ohne Kategorie lassen. tag_mapping_title: Tags zuweisen uploads: show: diff --git a/config/locales/views/imports/en.yml b/config/locales/views/imports/en.yml index 270b7d2f6..d97b7cf21 100644 --- a/config/locales/views/imports/en.yml +++ b/config/locales/views/imports/en.yml @@ -155,17 +155,17 @@ en: show: invalid_data: "You have invalid data, please edit until all errors are resolved" account_mapping_description: Assign all of your imported file's accounts to - Maybe's existing accounts. You can also add new accounts or leave them + %{product_name}'s existing accounts. You can also add new accounts or leave them uncategorized. account_mapping_title: Assign your accounts account_type_mapping_description: Assign all of your imported file's account - types to Maybe's + types to %{product_name}'s account_type_mapping_title: Assign your account types category_mapping_description: Assign all of your imported file's categories - to Maybe's existing categories. You can also add new categories or leave + to %{product_name}'s existing categories. You can also add new categories or leave them uncategorized. category_mapping_title: Assign your categories - tag_mapping_description: Assign all of your imported file's tags to Maybe's + tag_mapping_description: Assign all of your imported file's tags to %{product_name}'s existing tags. You can also add new tags or leave them uncategorized. tag_mapping_title: Assign your tags uploads: diff --git a/config/locales/views/imports/es.yml b/config/locales/views/imports/es.yml index b8f0c9ca5..d42aa010d 100644 --- a/config/locales/views/imports/es.yml +++ b/config/locales/views/imports/es.yml @@ -61,13 +61,13 @@ es: rows_label: Filas unassigned_account: ¿Necesitas crear una nueva cuenta para las filas sin asignar? show: - account_mapping_description: Asigna todas las cuentas de tu archivo importado a las cuentas existentes de Sure. También puedes añadir nuevas cuentas o dejarlas sin categorizar. + account_mapping_description: Asigna todas las cuentas de tu archivo importado a las cuentas existentes de %{product_name}. También puedes añadir nuevas cuentas o dejarlas sin categorizar. account_mapping_title: Asigna tus cuentas - account_type_mapping_description: Asigna todos los tipos de cuenta de tu archivo importado a los de Sure. + account_type_mapping_description: Asigna todos los tipos de cuenta de tu archivo importado a los de %{product_name}. account_type_mapping_title: Asigna tus tipos de cuenta - category_mapping_description: Asigna todas las categorías de tu archivo importado a las categorías existentes de Sure. También puedes añadir nuevas categorías o dejarlas sin categorizar. + category_mapping_description: Asigna todas las categorías de tu archivo importado a las categorías existentes de %{product_name}. También puedes añadir nuevas categorías o dejarlas sin categorizar. category_mapping_title: Asigna tus categorías - tag_mapping_description: Asigna todas las etiquetas de tu archivo importado a las etiquetas existentes de Sure. También puedes añadir nuevas etiquetas o dejarlas sin categorizar. + tag_mapping_description: Asigna todas las etiquetas de tu archivo importado a las etiquetas existentes de %{product_name}. También puedes añadir nuevas etiquetas o dejarlas sin categorizar. tag_mapping_title: Asigna tus etiquetas uploads: show: diff --git a/config/locales/views/imports/hu.yml b/config/locales/views/imports/hu.yml index 8221e18fe..543194625 100644 --- a/config/locales/views/imports/hu.yml +++ b/config/locales/views/imports/hu.yml @@ -134,13 +134,13 @@ hu: next: Tovább show: invalid_data: "Érvénytelen adatok találhatók, kérjük szerkeszd, amíg minden hibát ki nem javítasz" - account_mapping_description: Rendeld hozzá az importált fájl számláit a Maybe meglévő számláihoz. Új számlákat is hozzáadhatsz, vagy hagyhatod őket kategorizálatlanul. + account_mapping_description: Rendeld hozzá az importált fájl számláit a %{product_name} meglévő számláihoz. Új számlákat is hozzáadhatsz, vagy hagyhatod őket kategorizálatlanul. account_mapping_title: Számlák hozzárendelése - account_type_mapping_description: Rendeld hozzá az importált fájl számlatípusait a Maybe típusaihoz. + account_type_mapping_description: Rendeld hozzá az importált fájl számlatípusait a %{product_name} típusaihoz. account_type_mapping_title: Számlatípusok hozzárendelése - category_mapping_description: Rendeld hozzá az importált fájl kategóriáit a Maybe meglévő kategóriáihoz. Új kategóriákat is hozzáadhatsz, vagy hagyhatod őket kategorizálatlanul. + category_mapping_description: Rendeld hozzá az importált fájl kategóriáit a %{product_name} meglévő kategóriáihoz. Új kategóriákat is hozzáadhatsz, vagy hagyhatod őket kategorizálatlanul. category_mapping_title: Kategóriák hozzárendelése - tag_mapping_description: Rendeld hozzá az importált fájl címkéit a Maybe meglévő címkéihez. Új címkéket is hozzáadhatsz, vagy hagyhatod őket kategorizálatlanul. + tag_mapping_description: Rendeld hozzá az importált fájl címkéit a %{product_name} meglévő címkéihez. Új címkéket is hozzáadhatsz, vagy hagyhatod őket kategorizálatlanul. tag_mapping_title: Címkék hozzárendelése uploads: update: diff --git a/config/locales/views/imports/nb.yml b/config/locales/views/imports/nb.yml index cebd1ddb0..3e4f60610 100644 --- a/config/locales/views/imports/nb.yml +++ b/config/locales/views/imports/nb.yml @@ -31,18 +31,18 @@ nb: unassigned_account: Trenger å opprette en ny konto for utilordnede rader? show: account_mapping_description: Tilordne alle kontoene i den importerte filen din til - Maybes eksisterende kontoer. Du kan også legge til nye kontoer eller la dem + %{product_name}s eksisterende kontoer. Du kan også legge til nye kontoer eller la dem være ukategorisert. account_mapping_title: Tilordne kontoene dine account_type_mapping_description: Tilordne alle kontotypene i den importerte filen din til - Maybes + %{product_name}s account_type_mapping_title: Tilordne kontotypene dine category_mapping_description: Tilordne alle kategoriene i den importerte filen din til - Maybes eksisterende kategorier. Du kan også legge til nye kategorier eller la dem + %{product_name}s eksisterende kategorier. Du kan også legge til nye kategorier eller la dem være ukategorisert. category_mapping_title: Tilordne kategoriene dine tag_mapping_description: Tilordne alle tagene i den importerte filen din til - Maybes eksisterende tagger. Du kan også legge til nye tagger eller la dem + %{product_name}s eksisterende tagger. Du kan også legge til nye tagger eller la dem være ukategorisert. tag_mapping_title: Tilordne tagene dine uploads: diff --git a/config/locales/views/imports/nl.yml b/config/locales/views/imports/nl.yml index 3d40a4e90..31a2d3678 100644 --- a/config/locales/views/imports/nl.yml +++ b/config/locales/views/imports/nl.yml @@ -37,13 +37,13 @@ nl: rows_label: Rijen unassigned_account: Moet u een nieuw account aanmaken voor niet-toegewezen rijen? show: - account_mapping_description: Wijs alle accounts van uw geïmporteerde bestand toe aan bestaande %{product} accounts. U kunt ook nieuwe accounts toevoegen of ze ongecategoriseerd laten. + account_mapping_description: Wijs alle accounts van uw geïmporteerde bestand toe aan bestaande %{product_name} accounts. U kunt ook nieuwe accounts toevoegen of ze ongecategoriseerd laten. account_mapping_title: Uw accounts toewijzen - account_type_mapping_description: Wijs alle accounttypen van uw geïmporteerde bestand toe aan %{product} accounttypen + account_type_mapping_description: Wijs alle accounttypen van uw geïmporteerde bestand toe aan %{product_name} accounttypen account_type_mapping_title: Uw accounttypen toewijzen - category_mapping_description: Wijs alle categorieën van uw geïmporteerde bestand toe aan bestaande %{product} categorieën. U kunt ook nieuwe categorieën toevoegen of ze ongecategoriseerd laten. + category_mapping_description: Wijs alle categorieën van uw geïmporteerde bestand toe aan bestaande %{product_name} categorieën. U kunt ook nieuwe categorieën toevoegen of ze ongecategoriseerd laten. category_mapping_title: Uw categorieën toewijzen - tag_mapping_description: Wijs alle tags van uw geïmporteerde bestand toe aan bestaande %{product} tags. U kunt ook nieuwe tags toevoegen of ze ongecategoriseerd laten. + tag_mapping_description: Wijs alle tags van uw geïmporteerde bestand toe aan bestaande %{product_name} tags. U kunt ook nieuwe tags toevoegen of ze ongecategoriseerd laten. tag_mapping_title: Uw tags toewijzen uploads: show: diff --git a/config/locales/views/imports/pl.yml b/config/locales/views/imports/pl.yml index af8db190c..d5d1cd967 100644 --- a/config/locales/views/imports/pl.yml +++ b/config/locales/views/imports/pl.yml @@ -74,13 +74,13 @@ pl: rows_label: Wiersze unassigned_account: Chcesz utworzyć nowe konto dla nieprzypisanych wierszy? show: - account_mapping_description: Przypisz wszystkie konta z importowanego pliku do istniejących kont %{product}. Możesz też dodać nowe konta lub zostawić je bez przypisania. + account_mapping_description: Przypisz wszystkie konta z importowanego pliku do istniejących kont %{product_name}. Możesz też dodać nowe konta lub zostawić je bez przypisania. account_mapping_title: Przypisz konta - account_type_mapping_description: Przypisz wszystkie typy kont z importowanego pliku do typów używanych w %{product}. + account_type_mapping_description: Przypisz wszystkie typy kont z importowanego pliku do typów używanych w %{product_name}. account_type_mapping_title: Przypisz typy kont - category_mapping_description: Przypisz wszystkie kategorie z importowanego pliku do istniejących kategorii %{product}. Możesz też dodać nowe kategorie lub zostawić je bez przypisania. + category_mapping_description: Przypisz wszystkie kategorie z importowanego pliku do istniejących kategorii %{product_name}. Możesz też dodać nowe kategorie lub zostawić je bez przypisania. category_mapping_title: Przypisz kategorie - tag_mapping_description: Przypisz wszystkie tagi z importowanego pliku do istniejących tagów %{product}. Możesz też dodać nowe tagi lub zostawić je bez przypisania. + tag_mapping_description: Przypisz wszystkie tagi z importowanego pliku do istniejących tagów %{product_name}. Możesz też dodać nowe tagi lub zostawić je bez przypisania. tag_mapping_title: Przypisz tagi uploads: show: diff --git a/config/locales/views/imports/pt-BR.yml b/config/locales/views/imports/pt-BR.yml index 15b71dc0e..84edb7b83 100644 --- a/config/locales/views/imports/pt-BR.yml +++ b/config/locales/views/imports/pt-BR.yml @@ -36,18 +36,18 @@ pt-BR: unassigned_account: Precisa criar uma nova conta para linhas não atribuídas? show: account_mapping_description: Atribua todas as contas do seu arquivo importado às - contas existentes no %{product}. Você também pode adicionar novas contas ou deixá-las + contas existentes no %{product_name}. Você também pode adicionar novas contas ou deixá-las sem categoria. account_mapping_title: Atribuir suas contas account_type_mapping_description: Atribuir todos os tipos de conta do seu arquivo importado aos - do %{product} + do %{product_name} account_type_mapping_title: Atribuir seus tipos de conta category_mapping_description: Atribua todas as categorias do seu arquivo importado às - categorias existentes no %{product}. Você também pode adicionar novas categorias ou deixá-las + categorias existentes no %{product_name}. Você também pode adicionar novas categorias ou deixá-las sem categoria. category_mapping_title: Atribuir suas categorias tag_mapping_description: Atribua todas as tags do seu arquivo importado às - tags existentes no %{product}. Você também pode adicionar novas tags ou deixá-las + tags existentes no %{product_name}. Você também pode adicionar novas tags ou deixá-las sem categoria. tag_mapping_title: Atribuir suas tags uploads: diff --git a/config/locales/views/imports/ro.yml b/config/locales/views/imports/ro.yml index bec1cf695..b9783f815 100644 --- a/config/locales/views/imports/ro.yml +++ b/config/locales/views/imports/ro.yml @@ -26,13 +26,13 @@ ro: rows_label: Rânduri unassigned_account: Trebuie să creezi un cont nou pentru rândurile neasignate? show: - account_mapping_description: Asignează toate conturile din fișierul tău importat conturilor existente din Maybe. Poți adăuga și conturi noi sau le poți lăsa necategorizate. + account_mapping_description: Asignează toate conturile din fișierul tău importat conturilor existente din %{product_name}. Poți adăuga și conturi noi sau le poți lăsa necategorizate. account_mapping_title: Asignează-ți conturile - account_type_mapping_description: Asignează toate tipurile de cont din fișierul tău importat celor din Maybe. + account_type_mapping_description: Asignează toate tipurile de cont din fișierul tău importat celor din %{product_name}. account_type_mapping_title: Asignează-ți tipurile de cont - category_mapping_description: Asignează toate categoriile din fișierul tău importat categoriilor existente din Maybe. Poți adăuga și categorii noi sau le poți lăsa necategorizate. + category_mapping_description: Asignează toate categoriile din fișierul tău importat categoriilor existente din %{product_name}. Poți adăuga și categorii noi sau le poți lăsa necategorizate. category_mapping_title: Asignează-ți categoriile - tag_mapping_description: Asignează toate etichetele din fișierul tău importat etichetelor existente din Maybe. Poți adăuga și etichete noi sau le poți lăsa necategorizate. + tag_mapping_description: Asignează toate etichetele din fișierul tău importat etichetelor existente din %{product_name}. Poți adăuga și etichete noi sau le poți lăsa necategorizate. tag_mapping_title: Asignează-ți etichetele uploads: show: diff --git a/config/locales/views/imports/tr.yml b/config/locales/views/imports/tr.yml index 7aaaaf079..8296d655c 100644 --- a/config/locales/views/imports/tr.yml +++ b/config/locales/views/imports/tr.yml @@ -26,13 +26,13 @@ tr: rows_label: Satırlar unassigned_account: Atanmamış satırlar için yeni bir hesap oluşturmak ister misiniz? show: - account_mapping_description: "İçe aktardığınız dosyadaki tüm hesapları %{product}'deki mevcut hesaplara eşleyin. Ayrıca yeni hesaplar ekleyebilir veya kategorize etmeden bırakabilirsiniz." + account_mapping_description: "İçe aktardığınız dosyadaki tüm hesapları %{product_name}'deki mevcut hesaplara eşleyin. Ayrıca yeni hesaplar ekleyebilir veya kategorize etmeden bırakabilirsiniz." account_mapping_title: Hesaplarınızı eşleyin - account_type_mapping_description: "İçe aktardığınız dosyadaki tüm hesap türlerini %{product}'deki hesap türlerine eşleyin." + account_type_mapping_description: "İçe aktardığınız dosyadaki tüm hesap türlerini %{product_name}'deki hesap türlerine eşleyin." account_type_mapping_title: Hesap türlerinizi eşleyin - category_mapping_description: "İçe aktardığınız dosyadaki tüm kategorileri %{product}'deki mevcut kategorilere eşleyin. Ayrıca yeni kategoriler ekleyebilir veya kategorize etmeden bırakabilirsiniz." + category_mapping_description: "İçe aktardığınız dosyadaki tüm kategorileri %{product_name}'deki mevcut kategorilere eşleyin. Ayrıca yeni kategoriler ekleyebilir veya kategorize etmeden bırakabilirsiniz." category_mapping_title: Kategorilerinizi eşleyin - tag_mapping_description: "İçe aktardığınız dosyadaki tüm etiketleri %{product}'deki mevcut etiketlere eşleyin. Ayrıca yeni etiketler ekleyebilir veya kategorize etmeden bırakabilirsiniz." + tag_mapping_description: "İçe aktardığınız dosyadaki tüm etiketleri %{product_name}'deki mevcut etiketlere eşleyin. Ayrıca yeni etiketler ekleyebilir veya kategorize etmeden bırakabilirsiniz." tag_mapping_title: Etiketlerinizi eşleyin uploads: show: diff --git a/config/locales/views/imports/zh-CN.yml b/config/locales/views/imports/zh-CN.yml index 3c5493f86..8ea66765b 100644 --- a/config/locales/views/imports/zh-CN.yml +++ b/config/locales/views/imports/zh-CN.yml @@ -34,13 +34,13 @@ zh-CN: sure_mapping_label: "%{product_name} 中的 %{mapping}" unassigned_account: 需要为未分配行创建新账户? show: - account_mapping_description: 将导入文件中的所有账户分配到 %{product} 的现有账户。您也可以新建账户或保留为未分类。 + account_mapping_description: 将导入文件中的所有账户分配到 %{product_name} 的现有账户。您也可以新建账户或保留为未分类。 account_mapping_title: 分配账户 - account_type_mapping_description: 将导入文件中的所有账户类型分配到 %{product} 的账户类型 + account_type_mapping_description: 将导入文件中的所有账户类型分配到 %{product_name} 的账户类型 account_type_mapping_title: 分配账户类型 - category_mapping_description: 将导入文件中的所有分类分配到 %{product} 的现有分类。您也可以新建分类或保留为未分类。 + category_mapping_description: 将导入文件中的所有分类分配到 %{product_name} 的现有分类。您也可以新建分类或保留为未分类。 category_mapping_title: 分配分类 - tag_mapping_description: 将导入文件中的所有标签分配到 %{product} 的现有标签。您也可以新建标签或保留为未分类。 + tag_mapping_description: 将导入文件中的所有标签分配到 %{product_name} 的现有标签。您也可以新建标签或保留为未分类。 tag_mapping_title: 分配标签 uploads: show: diff --git a/config/locales/views/imports/zh-TW.yml b/config/locales/views/imports/zh-TW.yml index a7a47849b..6dbbba204 100644 --- a/config/locales/views/imports/zh-TW.yml +++ b/config/locales/views/imports/zh-TW.yml @@ -34,13 +34,13 @@ zh-TW: rows_label: 資料列 unassigned_account: 需要為未分配的資料列建立新帳戶嗎? show: - account_mapping_description: 將匯入檔案中的帳戶對應到 Maybe 現有的帳戶。您也可以新增帳戶,或暫時不進行分類。 + account_mapping_description: 將匯入檔案中的帳戶對應到 %{product_name} 現有的帳戶。您也可以新增帳戶,或暫時不進行分類。 account_mapping_title: 分配您的帳戶 - account_type_mapping_description: 將匯入檔案中的帳戶類型對應到 Maybe 的帳戶類型 + account_type_mapping_description: 將匯入檔案中的帳戶類型對應到 %{product_name} 的帳戶類型 account_type_mapping_title: 分配您的帳戶類型 - category_mapping_description: 將匯入檔案中的類別對應到 Maybe 現有的類別。您也可以新增類別,或暫時不進行分類。 + category_mapping_description: 將匯入檔案中的類別對應到 %{product_name} 現有的類別。您也可以新增類別,或暫時不進行分類。 category_mapping_title: 分配您的類別 - tag_mapping_description: 將匯入檔案中的標籤對應到 Maybe 現有的標籤。您也可以新增標籤,或暫時不進行分類。 + tag_mapping_description: 將匯入檔案中的標籤對應到 %{product_name} 現有的標籤。您也可以新增標籤,或暫時不進行分類。 tag_mapping_title: 分配您的標籤 uploads: show: From 2ce875f57f819eba8e52a44a68e929e849a7a2e8 Mon Sep 17 00:00:00 2001 From: "sentry[bot]" <39604003+sentry[bot]@users.noreply.github.com> Date: Sun, 24 May 2026 15:50:47 +0200 Subject: [PATCH 08/12] fix(messages): handle blank content submission gracefully (#1938) Co-authored-by: sentry[bot] <39604003+sentry[bot]@users.noreply.github.com> --- app/controllers/messages_controller.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb index b8041ad3d..f5e98ae41 100644 --- a/app/controllers/messages_controller.rb +++ b/app/controllers/messages_controller.rb @@ -4,13 +4,17 @@ class MessagesController < ApplicationController before_action :set_chat def create - @message = UserMessage.create!( + @message = UserMessage.new( chat: @chat, content: message_params[:content], ai_model: message_params[:ai_model].presence || Chat.default_model ) - redirect_to chat_path(@chat, thinking: true) + if @message.save + redirect_to chat_path(@chat, thinking: true) + else + redirect_to chat_path(@chat), alert: @message.errors.full_messages.to_sentence + end end private From 06518b49a122def9da0c84e25e6894c706b01dfe Mon Sep 17 00:00:00 2001 From: "sentry[bot]" <39604003+sentry[bot]@users.noreply.github.com> Date: Sun, 24 May 2026 15:51:18 +0200 Subject: [PATCH 09/12] fix(trades): prevent MissingTemplate for Turbo Stream requests on update/create failure (#1893) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(trades): prevent MissingTemplate for Turbo Stream requests on update/create failure * Linter noise --------- Co-authored-by: sentry[bot] <39604003+sentry[bot]@users.noreply.github.com> Co-authored-by: Juan José Mata --- app/controllers/trades_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/trades_controller.rb b/app/controllers/trades_controller.rb index e3a90121e..e3aed40db 100644 --- a/app/controllers/trades_controller.rb +++ b/app/controllers/trades_controller.rb @@ -35,7 +35,7 @@ class TradesController < ApplicationController format.turbo_stream { stream_redirect_back_or_to account_path(@account) } end else - render :new, status: :unprocessable_entity + render :new, status: :unprocessable_entity, formats: [ :html ] end end @@ -69,7 +69,7 @@ class TradesController < ApplicationController end end else - render :show, status: :unprocessable_entity + render :show, status: :unprocessable_entity, formats: [ :html ] end end From adea16f694481de9b6a5443b331c3d4c1384e97d Mon Sep 17 00:00:00 2001 From: Guillem Arias Fauste Date: Sun, 24 May 2026 16:05:14 +0200 Subject: [PATCH 10/12] fix(views): clear Rule 2 + Rule 5 findings from weekly DS drift (#1951) (#1955) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(views): clear Rule 2 + Rule 5 findings from weekly DS drift (#1951) Token swaps + i18n cleanup across the three files flagged in the weekly merged-commit drift scan. **`app/views/admin/users/index.html.erb`** - `bg-green-100 text-green-800` → `bg-success/10 text-success` (2 callsites — active-subscription badge + super_admin role legend) - `bg-surface-default` → `bg-surface` (`--color-surface-default` isn't defined; canonical token is `--color-surface`) - `bg-red-50/30 dark:bg-red-950/20` → `bg-destructive/5` (pending-invitation row highlight; functional token resolves correctly in both themes via `--color-destructive`) - Hand-rolled destructive button classes (`text-red-600`, `border-red-300`, `hover:bg-red-50`) → functional tokens (`text-destructive`, `border-destructive`, `hover:bg-destructive/10`) - Drop redundant `default:` args from `t(".roles.member", default: "Member")` and `t(".role_descriptions.member", default: "Basic user access…")` — the locale keys exist in `config/locales/views/admin/users/en.yml` **`app/views/imports/new.html.erb`** - `icon_bg_class: "bg-gray-tint-5"` → `"bg-surface-inset"` (`gray-tint-5` isn't a defined utility; `bg-surface-inset` carries the same muted-background intent and theme-swaps correctly) **`app/views/settings/profiles/show.html.erb`** - Drop redundant `default:` args from `t(".group_title", default: "Group")`, `t(".group_form_label", default: "Group name")`, and `t(".group_form_input_placeholder", default: "Enter group name")` — all three keys exist in `config/locales/views/settings/en.yml` **Deferred** to a separate PR (Rule 1 findings on admin/users): - `
` block (lines 54–180) → `DS::Disclosure(:card)` — bigger refactor with custom summary content + Stimulus controller attributes; warrants its own diff. - Destructive button shell → `DS::Button(:destructive)` — same reason; the class-token swap in this PR clears the immediate violation without changing the form-with structure or visual. Refs #1951. * fix(profiles): restore i18n default: args for group_* keys @jjmata + @codex correctly flagged: `settings.profiles.show.group_title`, `group_form_label`, and `group_form_input_placeholder` are defined in en.yml + 4 other locales (de, es, pl, pt-BR), but missing from 8 locales (ca, fr, nb, nl, ro, tr, zh-CN, zh-TW). With `config.i18n.fallbacks = true` those locales currently fall back to en values, so end-users see English copy rather than a translation-missing marker. The `default:` arg makes the fallback explicit at the call site without depending on the Rails fallback chain being configured a particular way — restores the original defensive behavior from before #1955. Admin/users role keys keep their `default:` removal — verified that `roles.member` and `role_descriptions.member` exist in all 8 admin/users locales (`grep -c "^\s*member:"` returns 2 for every locale file). --- app/views/admin/users/index.html.erb | 18 +++++++++--------- app/views/imports/new.html.erb | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/views/admin/users/index.html.erb b/app/views/admin/users/index.html.erb index c31a1fb61..5e5a0ec0a 100644 --- a/app/views/admin/users/index.html.erb +++ b/app/views/admin/users/index.html.erb @@ -12,7 +12,7 @@ <%= f.label :role, t(".filters.role"), class: "block text-sm font-medium text-primary mb-1" %> <%= f.select :role, options_for_select( - [[t(".filters.role_all"), ""], [t(".roles.guest"), "guest"], [t(".roles.member", default: "Member"), "member"], [t(".roles.admin"), "admin"], [t(".roles.super_admin"), "super_admin"]], + [[t(".filters.role_all"), ""], [t(".roles.guest"), "guest"], [t(".roles.member"), "member"], [t(".roles.admin"), "admin"], [t(".roles.super_admin"), "super_admin"]], params[:role] ), {}, @@ -75,7 +75,7 @@ <% elsif sub %> "> + <%= sub.active? ? "bg-success/10 text-success" : "bg-surface text-secondary" %>"> <%= sub.status.humanize %> <% else %> @@ -87,7 +87,7 @@
- + @@ -123,7 +123,7 @@ <%= form.select :role, options_for_select([ [t(".roles.guest"), "guest"], - [t(".roles.member", default: "Member"), "member"], + [t(".roles.member"), "member"], [t(".roles.admin"), "admin"], [t(".roles.super_admin"), "super_admin"] ], user.role), @@ -139,7 +139,7 @@ <% if pending_invitations.any? %> <% pending_invitations.each do |invitation| %> - +
<%= t(".table.user") %> <%= t(".table.last_login") %>
<%= icon "mail", class: "w-5 h-5 text-secondary shrink-0" %> @@ -160,7 +160,7 @@ <% end %> @@ -198,9 +198,9 @@
- <%= t(".roles.member", default: "Member") %> + <%= t(".roles.member") %> -

<%= t(".role_descriptions.member", default: "Basic user access. Can manage their own accounts, transactions, and settings.") %>

+

<%= t(".role_descriptions.member") %>

@@ -209,7 +209,7 @@

<%= t(".role_descriptions.admin") %>

- + <%= t(".roles.super_admin") %>

<%= t(".role_descriptions.super_admin") %>

diff --git a/app/views/imports/new.html.erb b/app/views/imports/new.html.erb index e71d84b6f..78950bd76 100644 --- a/app/views/imports/new.html.erb +++ b/app/views/imports/new.html.erb @@ -122,7 +122,7 @@ <%= render "imports/import_option", type: "TransactionImport", icon_name: "bar-chart-2", - icon_bg_class: "bg-gray-tint-5", + icon_bg_class: "bg-surface-inset", icon_text_class: "text-subdued", label: t(".import_ynab"), enabled: false, From 8c07236f715c30aedf5e89692240c8d4e078d64f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20Mata?= Date: Sun, 24 May 2026 16:19:37 +0200 Subject: [PATCH 11/12] Bump version by hand --- .sure-version | 2 +- charts/sure/Chart.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.sure-version b/.sure-version index 38d939e70..dafd26204 100644 --- a/.sure-version +++ b/.sure-version @@ -1 +1 @@ -0.7.1-alpha.10 +0.7.1-alpha.11 diff --git a/charts/sure/Chart.yaml b/charts/sure/Chart.yaml index 412a3c29e..7101091a8 100644 --- a/charts/sure/Chart.yaml +++ b/charts/sure/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: sure description: Official Helm chart for deploying the Sure Rails app (web + Sidekiq) on Kubernetes with optional HA PostgreSQL (CloudNativePG) and Redis. type: application -version: 0.7.1-alpha.10 -appVersion: "0.7.1-alpha.10" +version: 0.7.1-alpha.11 +appVersion: "0.7.1-alpha.11" kubeVersion: ">=1.25.0-0" From 89f42497a9951fbb72ce087dc813ed9973f9e686 Mon Sep 17 00:00:00 2001 From: "Sure Admin (bot)" Date: Sun, 24 May 2026 17:36:17 +0200 Subject: [PATCH 12/12] fix: invert non-gittensor label condition (#1960) --- .github/workflows/label-not-gittensor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/label-not-gittensor.yml b/.github/workflows/label-not-gittensor.yml index bfd74f822..7b846e8f7 100644 --- a/.github/workflows/label-not-gittensor.yml +++ b/.github/workflows/label-not-gittensor.yml @@ -39,7 +39,7 @@ jobs: const users = new Set(parseList(process.env.GITTENSOR_USERS, 'GITTENSOR_USERS')); const exceptions = new Set(parseList(process.env.GITTENSOR_EXCEPTIONS, 'GITTENSOR_EXCEPTIONS')); - if (!users.has(author) || exceptions.has(author)) { + if (users.has(author) || exceptions.has(author)) { core.info(`No label needed for @${author}.`); return; }