1
1
مراية لـ https://github.com/postalserver/postal.git تم المزامنة 2026-06-17 19:55:43 +00:00

Compare commits

..

24 الالتزامات

المؤلف SHA1 الرسالة التاريخ
github-actions[bot]
8ef89606bc chore(main): release 3.3.6 (#3558)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-28 17:19:33 +01:00
Adam Cooke
84f4e20f05 refactor(auth): tighten return_to validation
url_with_return_to only checked that return_to started with a forward
slash, which also allowed protocol-relative values like //host and
/\host. Rails 7.1 already refuses to follow those via redirect_to, so
the user just saw a 500. Reject the same shapes in the helper instead
so we fall back to the default URL cleanly.

Adds a sessions request spec covering the rejected shapes plus the
happy-path relative redirect.
2026-04-24 23:03:50 +01:00
Adam Cooke
9243524035 refactor(helpers): escape interpolated values in select options
The endpoint and domain option helpers interpolated model attributes
straight into an HTML string before marking the whole buffer html_safe.
Wrap the interpolations in h() so untrusted attributes can't break out
of the surrounding tag.

Also stop the helpers glob in rails_helper from eagerly requiring
_spec.rb files so helper specs can live under spec/helpers/, and add a
small application helper spec covering the escape behaviour.
2026-04-24 22:55:46 +01:00
Adam Cooke
dca7f90b90 refactor(tracking): remove unused src image proxy
The /img/<server>/<message> endpoint accepted a src=<url> query
parameter and proxied the body of that URL back to the caller. Nothing
in the codebase ever produces a src= parameter — the parser only
inserts a plain tracking pixel and rewrites href links — so this branch
is dead code inherited from the original AppMail import.

Drop the src branch: requests with src now return 400. The no-src path
that serves the tracking pixel and records loads is unchanged, and a
spec covers both the pixel-serving path and the removed branch.
2026-04-24 22:24:18 +01:00
Adam Cooke
cad2aa6808 fix(messages): sandbox rendered email HTML as extra XSS defence
The app-wide CSP already blocks inline script execution, but the HTML
preview iframe for a stored email was same-origin and un-sandboxed, and
the html_raw response had no per-action hardening. Add a sandbox on the
iframe and tighten the CSP on html_raw to script-src 'none' with
nosniff and no-referrer so the preview has defence in depth against a
future CSP bypass or regression.

Relates to GHSA-f6g9-8555-cw28.
2026-04-24 22:12:27 +01:00
Adam Cooke
b611d577af chore: ignore node modules and yarn.lock 2026-04-24 21:34:57 +01:00
github-actions[bot]
d532922ff7 chore(main): release 3.3.5 (#3208)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-01 14:55:42 +00:00
Adam Cooke
11419f9914 fix(deliveries): escape delivery details to prevent HTML injection 2026-02-01 14:48:54 +00:00
Melle Douwsma
b7e5232e07 fix: typo in process logging (#3212)
Signed-off-by: Melle Douwsma <melledouwsma@users.noreply.github.com>
2025-10-03 09:41:20 +01:00
Johan Kok
e00098b800 fix: update url for v2 config (#3225)
This minor update resolves #3048

Signed-off-by: Johan Kok <johankok@users.noreply.github.com>
2025-10-03 09:40:22 +01:00
Adam Cooke
d00d978872 chore: upgrade resolv to 0.6.2 2025-10-02 14:39:27 +01:00
Adam Cooke
c78000ca8f chore: remove version from docker-compose.yml 2025-10-02 14:38:42 +01:00
Adam Cooke
c03c44b442 chore(deps): upgrade puma, net-imap and other deps 2025-10-01 18:13:36 +01:00
Adam Cooke
86de372382 chore(dockerfile): reduce container size 2025-10-01 18:12:26 +01:00
Adam Cooke
7c47422c86 fix(health_server): use rackup handler instead of rack handler 2025-10-01 18:12:26 +01:00
Arthur Lutz
f5325c49ff docs(process.rb): add help about time unit used by metric (#3339)
Signed-off-by: Arthur Lutz <arthur.lutz@zenika.com>
2025-10-01 17:31:13 +01:00
Adam Cooke
f193b8e77f chore: upgrade uri gem to 1.0.3 2025-10-01 16:47:59 +01:00
Adam Cooke
ab6d4430ba chore: upgrade to rails 7.1 and ruby 3.4 (#3457) 2025-10-01 16:42:39 +01:00
Matthieu Barthel
9c5f96ae90 fix: oidc scopes are invalid when concatenated (#3332) 2025-05-08 07:51:46 +01:00
Som23Git
fd3c7ccdf6 fix: typo in the credentials page 2024-10-31 17:53:20 +00:00
github-actions[bot]
da90e75036 chore(main): release 3.3.4 (#3014)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-20 14:39:58 +01:00
Adam Cooke
2b0919c145 fix: raise NotImplementedError when no call method on a scheduled task 2024-06-20 14:27:20 +01:00
Adam Cooke
3a33e53d84 fix: fix issue running message pruning task 2024-06-20 14:27:20 +01:00
Adam Cooke
4fa88acea0 fix: fix postal version command 2024-06-20 14:27:18 +01:00
37 ملفات معدلة مع 553 إضافات و201 حذوفات

عرض الملف

@@ -52,10 +52,10 @@ jobs:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- run: docker-compose pull
- run: docker compose pull
env:
POSTAL_IMAGE: ghcr.io/postalserver/postal:ci-${{ github.sha }}
- run: docker-compose run postal sh -c 'bundle exec rspec'
- run: docker compose run postal sh -c 'bundle exec rspec'
env:
POSTAL_IMAGE: ghcr.io/postalserver/postal:ci-${{ github.sha }}

3
.gitignore مباع
عرض الملف

@@ -34,3 +34,6 @@ BRANCH
.rubocop-https*
.env*
node_modules
yarn.lock

عرض الملف

@@ -1,3 +1,3 @@
{
".": "3.3.3"
".": "3.3.6"
}

عرض الملف

@@ -1 +1 @@
3.2.2
3.4.6

عرض الملف

@@ -2,6 +2,61 @@
This file contains all the latest changes and updates to Postal.
## [3.3.6](https://github.com/postalserver/postal/compare/3.3.5...3.3.6) (2026-04-28)
### Bug Fixes
* **messages:** sandbox rendered email HTML as extra XSS defence ([cad2aa6](https://github.com/postalserver/postal/commit/cad2aa6808519a3ff25215f09f4966d9fa3bb372))
### Miscellaneous Chores
* ignore node modules and yarn.lock ([b611d57](https://github.com/postalserver/postal/commit/b611d577af79b8e1e75b6d47fa04d1ba03e34eec))
### Code Refactoring
* **auth:** tighten return_to validation ([84f4e20](https://github.com/postalserver/postal/commit/84f4e20f05db2d11b0144f95960c956f8221e657))
* **helpers:** escape interpolated values in select options ([9243524](https://github.com/postalserver/postal/commit/924352403553dcfcc569876ca76c219493fac9d6))
* **tracking:** remove unused src image proxy ([dca7f90](https://github.com/postalserver/postal/commit/dca7f90b9046247c0d953567be35921167e79d87))
## [3.3.5](https://github.com/postalserver/postal/compare/3.3.4...3.3.5) (2026-02-01)
### Bug Fixes
* **deliveries:** escape delivery details to prevent HTML injection ([11419f9](https://github.com/postalserver/postal/commit/11419f99140e13688a9613cab3ee03f8d3cbae45))
* **health_server:** use rackup handler instead of rack handler ([7c47422](https://github.com/postalserver/postal/commit/7c47422c865e738c4d6af0fed1cca4405288341f))
* oidc scopes are invalid when concatenated ([#3332](https://github.com/postalserver/postal/issues/3332)) ([9c5f96a](https://github.com/postalserver/postal/commit/9c5f96ae90cf06dcd5db776806865752f667bd95))
* typo in process logging ([#3212](https://github.com/postalserver/postal/issues/3212)) ([b7e5232](https://github.com/postalserver/postal/commit/b7e5232e077b3c9b7a999dcb6676fba0ec61458e))
* typo in the credentials page ([fd3c7cc](https://github.com/postalserver/postal/commit/fd3c7ccdf6dc4ee0a76c9523cbd735159e4b8000))
* update url for v2 config ([#3225](https://github.com/postalserver/postal/issues/3225)) ([e00098b](https://github.com/postalserver/postal/commit/e00098b8003cf37f2708f536871b3ade377aed2d))
### Documentation
* **process.rb:** add help about time unit used by metric ([#3339](https://github.com/postalserver/postal/issues/3339)) ([f5325c4](https://github.com/postalserver/postal/commit/f5325c49ff1152ad53eaaec98717ad3412d379ae))
### Miscellaneous Chores
* **deps:** upgrade puma, net-imap and other deps ([c03c44b](https://github.com/postalserver/postal/commit/c03c44b442a29aa9881c1e1aae60bead9776a6b6))
* **dockerfile:** reduce container size ([86de372](https://github.com/postalserver/postal/commit/86de372382bd62bdd5d1372254f8817b0360bd56))
* remove version from docker-compose.yml ([c78000c](https://github.com/postalserver/postal/commit/c78000ca8f2998aa04648f465060768db6467de6))
* upgrade resolv to 0.6.2 ([d00d978](https://github.com/postalserver/postal/commit/d00d978872a96369544303d08f6a9d11cdf56b62))
* upgrade to rails 7.1 and ruby 3.4 ([#3457](https://github.com/postalserver/postal/issues/3457)) ([ab6d443](https://github.com/postalserver/postal/commit/ab6d4430baa33a05f1aa66e776cc2a5bcaa0ede8))
* upgrade uri gem to 1.0.3 ([f193b8e](https://github.com/postalserver/postal/commit/f193b8e77fc096382ab7aaa6a2c29641b4cb12df))
## [3.3.4](https://github.com/postalserver/postal/compare/3.3.3...3.3.4) (2024-06-20)
### Bug Fixes
* fix `postal version` command ([4fa88ac](https://github.com/postalserver/postal/commit/4fa88acea0dececd0eae485506a2ad8268fbea59))
* fix issue running message pruning task ([3a33e53](https://github.com/postalserver/postal/commit/3a33e53d843584757bb00898746aa059d7616db4))
* raise NotImplementedError when no call method on a scheduled task ([2b0919c](https://github.com/postalserver/postal/commit/2b0919c1454eabea93db96f50ecbd8e36bb89f1f))
## [3.3.3](https://github.com/postalserver/postal/compare/3.3.2...3.3.3) (2024-04-18)

عرض الملف

@@ -1,22 +1,25 @@
FROM ruby:3.2.2-bullseye AS base
FROM ruby:3.4.6-slim-bookworm AS base
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
software-properties-common dirmngr apt-transport-https \
&& (curl -sL https://deb.nodesource.com/setup_20.x | bash -) \
&& apt-get install --no-install-recommends -y curl \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN (curl -sL https://deb.nodesource.com/setup_20.x | bash -)
# Install main dependencies
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
netcat \
curl \
netcat-openbsd \
libmariadb-dev \
libcap2-bin \
nano \
nodejs
libyaml-dev \
nodejs \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN setcap 'cap_net_bind_service=+ep' /usr/local/bin/ruby
@@ -31,7 +34,7 @@ RUN mkdir -p /opt/postal/app /opt/postal/config
WORKDIR /opt/postal/app
# Install bundler
RUN gem install bundler -v 2.5.6 --no-doc
RUN gem install bundler -v 2.7.2 --no-doc
# Install the latest and active gem dependencies and re-run
# the appropriate commands to handle installs.

18
Gemfile
عرض الملف

@@ -1,6 +1,7 @@
# frozen_string_literal: true
source "https://rubygems.org"
gem "abbrev"
gem "authie"
gem "autoprefixer-rails"
gem "bcrypt"
@@ -17,14 +18,18 @@ gem "jwt"
gem "kaminari"
gem "klogger-logger"
gem "konfig-config", "~> 3.0"
gem "logger"
gem "mail"
gem "mutex_m"
gem "mysql2"
gem "nifty-utils"
gem "nilify_blanks"
gem "nio4r"
gem "ostruct"
gem "prometheus-client"
gem "puma"
gem "rails", "= 7.0.8.1"
gem "rackup"
gem "rails", "= 7.1.5.2"
gem "resolv"
gem "secure_headers"
gem "sentry-rails"
@@ -47,12 +52,15 @@ end
group :development do
gem "annotate"
gem "database_cleaner", require: false
gem "factory_bot_rails", require: false
gem "rspec", require: false
gem "rspec-rails", require: false
gem "rubocop"
gem "rubocop-rails"
end
group :test do
gem "database_cleaner-active_record"
gem "factory_bot_rails"
gem "rspec"
gem "rspec-rails"
gem "shoulda-matchers"
gem "timecop"
gem "webmock"

عرض الملف

@@ -1,70 +1,83 @@
GEM
remote: https://rubygems.org/
specs:
actioncable (7.0.8.1)
actionpack (= 7.0.8.1)
activesupport (= 7.0.8.1)
abbrev (0.1.2)
actioncable (7.1.5.2)
actionpack (= 7.1.5.2)
activesupport (= 7.1.5.2)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (7.0.8.1)
actionpack (= 7.0.8.1)
activejob (= 7.0.8.1)
activerecord (= 7.0.8.1)
activestorage (= 7.0.8.1)
activesupport (= 7.0.8.1)
zeitwerk (~> 2.6)
actionmailbox (7.1.5.2)
actionpack (= 7.1.5.2)
activejob (= 7.1.5.2)
activerecord (= 7.1.5.2)
activestorage (= 7.1.5.2)
activesupport (= 7.1.5.2)
mail (>= 2.7.1)
net-imap
net-pop
net-smtp
actionmailer (7.0.8.1)
actionpack (= 7.0.8.1)
actionview (= 7.0.8.1)
activejob (= 7.0.8.1)
activesupport (= 7.0.8.1)
actionmailer (7.1.5.2)
actionpack (= 7.1.5.2)
actionview (= 7.1.5.2)
activejob (= 7.1.5.2)
activesupport (= 7.1.5.2)
mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
rails-dom-testing (~> 2.0)
actionpack (7.0.8.1)
actionview (= 7.0.8.1)
activesupport (= 7.0.8.1)
rack (~> 2.0, >= 2.2.4)
rails-dom-testing (~> 2.2)
actionpack (7.1.5.2)
actionview (= 7.1.5.2)
activesupport (= 7.1.5.2)
nokogiri (>= 1.8.5)
racc
rack (>= 2.2.4)
rack-session (>= 1.0.1)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (7.0.8.1)
actionpack (= 7.0.8.1)
activerecord (= 7.0.8.1)
activestorage (= 7.0.8.1)
activesupport (= 7.0.8.1)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
actiontext (7.1.5.2)
actionpack (= 7.1.5.2)
activerecord (= 7.1.5.2)
activestorage (= 7.1.5.2)
activesupport (= 7.1.5.2)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
actionview (7.0.8.1)
activesupport (= 7.0.8.1)
actionview (7.1.5.2)
activesupport (= 7.1.5.2)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
activejob (7.0.8.1)
activesupport (= 7.0.8.1)
erubi (~> 1.11)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
activejob (7.1.5.2)
activesupport (= 7.1.5.2)
globalid (>= 0.3.6)
activemodel (7.0.8.1)
activesupport (= 7.0.8.1)
activerecord (7.0.8.1)
activemodel (= 7.0.8.1)
activesupport (= 7.0.8.1)
activestorage (7.0.8.1)
actionpack (= 7.0.8.1)
activejob (= 7.0.8.1)
activerecord (= 7.0.8.1)
activesupport (= 7.0.8.1)
activemodel (7.1.5.2)
activesupport (= 7.1.5.2)
activerecord (7.1.5.2)
activemodel (= 7.1.5.2)
activesupport (= 7.1.5.2)
timeout (>= 0.4.0)
activestorage (7.1.5.2)
actionpack (= 7.1.5.2)
activejob (= 7.1.5.2)
activerecord (= 7.1.5.2)
activesupport (= 7.1.5.2)
marcel (~> 1.0)
mini_mime (>= 1.1.0)
activesupport (7.0.8.1)
activesupport (7.1.5.2)
base64
benchmark (>= 0.3)
bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
logger (>= 1.4.2)
minitest (>= 5.1)
mutex_m
securerandom (>= 0.3)
tzinfo (~> 2.0)
addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0)
@@ -74,13 +87,14 @@ GEM
rake (>= 10.4, < 14.0)
ast (2.4.2)
attr_required (1.0.2)
authie (4.1.3)
activerecord (>= 6.1, < 8.0)
authie (5.0.0)
activerecord (>= 6.1, < 9.0)
autoprefixer-rails (10.4.13.0)
execjs (~> 2)
base64 (0.2.0)
base64 (0.3.0)
bcrypt (3.1.20)
bigdecimal (3.1.6)
benchmark (0.4.1)
bigdecimal (3.2.3)
bindata (2.5.0)
builder (3.2.4)
chronic (0.10.2)
@@ -91,26 +105,25 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.12.2)
concurrent-ruby (1.2.3)
concurrent-ruby (1.3.5)
connection_pool (2.5.4)
crack (1.0.0)
bigdecimal
rexml
crass (1.0.6)
database_cleaner (2.0.2)
database_cleaner-active_record (>= 2, < 3)
database_cleaner-active_record (2.1.0)
database_cleaner-active_record (2.2.2)
activerecord (>= 5.a)
database_cleaner-core (~> 2.0.0)
database_cleaner-core (~> 2.0)
database_cleaner-core (2.0.1)
date (3.3.4)
diff-lcs (1.5.0)
date (3.4.1)
diff-lcs (1.6.2)
domain_name (0.6.20240107)
dotenv (3.0.2)
dynamic_form (1.3.1)
actionview (> 5.2.0)
activemodel (> 5.2.0)
drb (2.2.3)
dynamic_form (1.2.0)
email_validator (2.2.4)
activemodel
erb (5.0.2)
erubi (1.12.0)
execjs (2.7.0)
factory_bot (6.4.6)
@@ -127,7 +140,7 @@ GEM
ffi (1.15.5)
gelf (3.1.0)
json
globalid (1.2.1)
globalid (1.3.0)
activesupport (>= 6.1)
haml (6.3.0)
temple (>= 0.8.2)
@@ -136,8 +149,13 @@ GEM
hashdiff (1.1.0)
hashie (5.0.0)
highline (2.1.0)
i18n (1.14.1)
i18n (1.14.7)
concurrent-ruby (~> 1.0)
io-console (0.8.1)
irb (1.15.2)
pp (>= 0.6.0)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
jquery-rails (4.5.1)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
@@ -170,7 +188,8 @@ GEM
rouge (>= 3.30, < 5.0)
konfig-config (3.0.0)
hashie
loofah (2.22.0)
logger (1.7.0)
loofah (2.24.1)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
mail (2.8.1)
@@ -178,15 +197,14 @@ GEM
net-imap
net-pop
net-smtp
marcel (1.0.2)
method_source (1.0.0)
marcel (1.1.0)
mini_mime (1.1.5)
mini_portile2 (2.8.5)
minitest (5.22.2)
minitest (5.25.5)
mutex_m (0.3.0)
mysql2 (0.5.6)
net-http (0.4.1)
uri
net-imap (0.4.10)
net-imap (0.5.11)
date
net-protocol
net-pop (0.1.2)
@@ -199,17 +217,14 @@ GEM
nilify_blanks (1.4.0)
activerecord (>= 4.0.0)
activesupport (>= 4.0.0)
nio4r (2.7.0)
nokogiri (1.16.2)
mini_portile2 (~> 2.8.2)
nio4r (2.7.4)
nokogiri (1.18.10-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.16.2-aarch64-linux)
nokogiri (1.18.10-arm64-darwin)
racc (~> 1.4)
nokogiri (1.16.2-arm64-darwin)
nokogiri (1.18.10-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.16.2-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.16.2-x86_64-linux)
nokogiri (1.18.10-x86_64-linux-gnu)
racc (~> 1.4)
omniauth (2.1.2)
hashie (>= 3.4.6)
@@ -234,15 +249,22 @@ GEM
tzinfo
validate_url
webfinger (~> 2.0)
ostruct (0.6.3)
parallel (1.22.1)
parser (3.2.1.1)
ast (~> 2.4.1)
pp (0.6.2)
prettyprint
prettyprint (0.2.0)
prometheus-client (4.2.2)
psych (5.2.6)
date
stringio
public_suffix (5.0.4)
puma (6.4.2)
puma (7.0.4)
nio4r (~> 2.0)
racc (1.7.3)
rack (2.2.8.1)
racc (1.8.1)
rack (3.2.1)
rack-oauth2 (2.2.1)
activesupport
attr_required
@@ -250,57 +272,69 @@ GEM
faraday-follow_redirects
json-jwt (>= 1.11.0)
rack (>= 2.1.0)
rack-protection (3.2.0)
rack-protection (4.1.1)
base64 (>= 0.1.0)
rack (~> 2.2, >= 2.2.4)
logger (>= 1.6.0)
rack (>= 3.0.0, < 4)
rack-session (2.1.1)
base64 (>= 0.1.0)
rack (>= 3.0.0)
rack-test (2.1.0)
rack (>= 1.3)
rails (7.0.8.1)
actioncable (= 7.0.8.1)
actionmailbox (= 7.0.8.1)
actionmailer (= 7.0.8.1)
actionpack (= 7.0.8.1)
actiontext (= 7.0.8.1)
actionview (= 7.0.8.1)
activejob (= 7.0.8.1)
activemodel (= 7.0.8.1)
activerecord (= 7.0.8.1)
activestorage (= 7.0.8.1)
activesupport (= 7.0.8.1)
rackup (2.2.1)
rack (>= 3)
rails (7.1.5.2)
actioncable (= 7.1.5.2)
actionmailbox (= 7.1.5.2)
actionmailer (= 7.1.5.2)
actionpack (= 7.1.5.2)
actiontext (= 7.1.5.2)
actionview (= 7.1.5.2)
activejob (= 7.1.5.2)
activemodel (= 7.1.5.2)
activerecord (= 7.1.5.2)
activestorage (= 7.1.5.2)
activesupport (= 7.1.5.2)
bundler (>= 1.15.0)
railties (= 7.0.8.1)
railties (= 7.1.5.2)
rails-dom-testing (2.2.0)
activesupport (>= 5.0.0)
minitest
nokogiri (>= 1.6)
rails-html-sanitizer (1.6.0)
rails-html-sanitizer (1.6.2)
loofah (~> 2.21)
nokogiri (~> 1.14)
railties (7.0.8.1)
actionpack (= 7.0.8.1)
activesupport (= 7.0.8.1)
method_source
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)
railties (7.1.5.2)
actionpack (= 7.1.5.2)
activesupport (= 7.1.5.2)
irb
rackup (>= 1.0.0)
rake (>= 12.2)
thor (~> 1.0)
zeitwerk (~> 2.5)
thor (~> 1.0, >= 1.2.2)
zeitwerk (~> 2.6)
rainbow (3.1.1)
rake (13.1.0)
rdoc (6.14.2)
erb
psych (>= 4.0.0)
regexp_parser (2.7.0)
resolv (0.3.0)
rexml (3.2.5)
reline (0.6.2)
io-console (~> 0.5)
resolv (0.6.2)
rexml (3.4.4)
rouge (4.2.0)
rspec (3.12.0)
rspec-core (~> 3.12.0)
rspec-expectations (~> 3.12.0)
rspec-mocks (~> 3.12.0)
rspec-core (3.12.1)
rspec-support (~> 3.12.0)
rspec-expectations (3.12.2)
rspec (3.13.1)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
rspec-mocks (~> 3.13.0)
rspec-core (3.13.5)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.5)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-mocks (3.12.4)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.5)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-support (~> 3.13.0)
rspec-rails (6.1.1)
actionpack (>= 6.1)
activesupport (>= 6.1)
@@ -309,7 +343,7 @@ GEM
rspec-expectations (~> 3.12)
rspec-mocks (~> 3.12)
rspec-support (~> 3.12)
rspec-support (3.12.0)
rspec-support (3.13.6)
rubocop (1.48.1)
json (~> 2.3)
parallel (~> 1.10)
@@ -338,6 +372,7 @@ GEM
sprockets-rails
tilt
secure_headers (6.5.0)
securerandom (0.4.1)
sentry-rails (5.16.1)
railties (>= 5.0)
sentry-ruby (~> 5.16.1)
@@ -352,6 +387,7 @@ GEM
actionpack (>= 5.2)
activesupport (>= 5.2)
sprockets (>= 3.0.0)
stringio (3.1.7)
swd (2.0.3)
activesupport (>= 3)
attr_required (>= 0.0.5)
@@ -361,7 +397,7 @@ GEM
thor (1.3.0)
tilt (2.3.0)
timecop (0.9.8)
timeout (0.4.1)
timeout (0.4.3)
turbolinks (5.2.1)
turbolinks-source (~> 5.2)
turbolinks-source (5.2.0)
@@ -370,7 +406,7 @@ GEM
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
unicode-display_width (2.4.2)
uri (0.13.0)
uri (1.0.3)
validate_url (1.0.15)
activemodel (>= 3.0.0)
public_suffix
@@ -382,8 +418,9 @@ GEM
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
webrick (1.8.1)
websocket-driver (0.7.6)
webrick (1.9.1)
websocket-driver (0.8.0)
base64
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
zeitwerk (2.6.13)
@@ -391,18 +428,18 @@ GEM
PLATFORMS
aarch64-linux
arm64-darwin
ruby
x86_64-darwin
x86_64-linux
DEPENDENCIES
abbrev
annotate
authie
autoprefixer-rails
bcrypt
chronic
coffee-rails (~> 5.0)
database_cleaner
database_cleaner-active_record
domain_name
dotenv
dynamic_form
@@ -417,16 +454,20 @@ DEPENDENCIES
kaminari
klogger-logger
konfig-config (~> 3.0)
logger
mail
mutex_m
mysql2
nifty-utils
nilify_blanks
nio4r
omniauth-rails_csrf_protection
omniauth_openid_connect
ostruct
prometheus-client
puma
rails (= 7.0.8.1)
rackup
rails (= 7.1.5.2)
resolv
rspec
rspec-rails
@@ -443,4 +484,4 @@ DEPENDENCIES
webrick
BUNDLED WITH
2.5.6
2.7.2

عرض الملف

@@ -62,10 +62,13 @@ class ApplicationController < ActionController::Base
end
def url_with_return_to(url)
if params[:return_to].blank? || !params[:return_to].starts_with?("/")
return_to = params[:return_to]
if return_to.blank? ||
!return_to.start_with?("/") ||
return_to.start_with?("//", "/\\")
url_for(url)
else
params[:return_to]
return_to
end
end

عرض الملف

@@ -89,6 +89,18 @@ class MessagesController < ApplicationController
end
def html_raw
override_content_security_policy_directives(
default_src: %w('none'),
script_src: %w('none'),
style_src: %w('unsafe-inline'),
img_src: %w(* data:),
font_src: %w(*),
frame_ancestors: %w('self'),
form_action: %w('none'),
base_uri: %w('none')
)
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["Referrer-Policy"] = "no-referrer"
render html: @message.html_body_without_tracking_image.html_safe
end

عرض الملف

@@ -3,6 +3,7 @@
module ApplicationHelper
def format_delivery_details(server, text)
text = h(text)
text.gsub!(/<msg:(\d+)>/) do
id = ::Regexp.last_match(1).to_i
link_to("message ##{id}", organization_server_message_path(server.organization, server, id), class: "u-link")
@@ -32,7 +33,7 @@ module ApplicationHelper
s << "<optgroup label='Server Domains'>"
server_domains.each do |domain|
selected = domain == selected_domain ? "selected='selected'" : ""
s << "<option value='#{domain.id}' #{selected}>#{domain.name}</option>"
s << "<option value='#{h(domain.id)}' #{selected}>#{h(domain.name)}</option>"
end
s << "</optgroup>"
end
@@ -42,7 +43,7 @@ module ApplicationHelper
s << "<optgroup label='Organization Domains'>"
organization_domains.each do |domain|
selected = domain == selected_domain ? "selected='selected'" : ""
s << "<option value='#{domain.id}' #{selected}>#{domain.name}</option>"
s << "<option value='#{h(domain.id)}' #{selected}>#{h(domain.name)}</option>"
end
s << "</optgroup>"
end
@@ -59,7 +60,7 @@ module ApplicationHelper
http_endpoints.each do |endpoint|
value = "#{endpoint.class}##{endpoint.uuid}"
selected = value == selected_value ? "selected='selected'" : ""
s << "<option value='#{value}' #{selected}>#{endpoint.description}</option>"
s << "<option value='#{h(value)}' #{selected}>#{h(endpoint.description)}</option>"
end
s << "</optgroup>"
end
@@ -70,7 +71,7 @@ module ApplicationHelper
smtp_endpoints.each do |endpoint|
value = "#{endpoint.class}##{endpoint.uuid}"
selected = value == selected_value ? "selected='selected'" : ""
s << "<option value='#{value}' #{selected}>#{endpoint.description}</option>"
s << "<option value='#{h(value)}' #{selected}>#{h(endpoint.description)}</option>"
end
s << "</optgroup>"
end
@@ -81,7 +82,7 @@ module ApplicationHelper
address_endpoints.each do |endpoint|
value = "#{endpoint.class}##{endpoint.uuid}"
selected = value == selected_value ? "selected='selected'" : ""
s << "<option value='#{value}' #{selected}>#{endpoint.address}</option>"
s << "<option value='#{h(value)}' #{selected}>#{h(endpoint.address)}</option>"
end
s << "</optgroup>"
end
@@ -93,7 +94,7 @@ module ApplicationHelper
selected = (selected_value == mode ? "selected='selected'" : "")
text = t("route_modes.#{mode.underscore}")
s << "<option value='#{mode}' #{selected}>#{text}</option>"
s << "<option value='#{h(mode)}' #{selected}>#{h(text)}</option>"
end
s << "</optgroup>"
end

عرض الملف

@@ -202,7 +202,7 @@ module Worker
logger.info "stopping tasks thread"
ActiveRecord::Base.connection_pool.with_connection do
if WorkerRole.release(:tasks)
logger.info "releasesd tasks role"
logger.info "released tasks role"
end
end
end
@@ -298,7 +298,7 @@ module Worker
labels: [:thread, :job]
register_prometheus_histogram :postal_worker_job_runtime,
docstring: "The time taken to process jobs",
docstring: "The time taken to process jobs (in seconds)",
labels: [:thread, :job]
register_prometheus_counter :postal_worker_errors,
@@ -306,11 +306,11 @@ module Worker
labels: [:error]
register_prometheus_histogram :postal_worker_task_runtime,
docstring: "The time taken to process tasks",
docstring: "The time taken to process tasks (in seconds)",
labels: [:task]
register_prometheus_histogram :postal_message_queue_latency,
docstring: "The length of time between a message being queued and being dequeued"
docstring: "The length of time between a message being queued and being dequeued (in seconds)"
end
end

عرض الملف

@@ -31,7 +31,7 @@ class Credential < ApplicationRecord
validate :validate_key_cannot_be_changed
validate :validate_key_for_smtp_ip
serialize :options, Hash
serialize :options, type: Hash
before_validation :generate_key

عرض الملف

@@ -34,7 +34,7 @@ class WebhookRequest < ApplicationRecord
validates :url, presence: true
validates :event, presence: true
serialize :payload, Hash
serialize :payload, type: Hash
class << self

عرض الملف

@@ -7,7 +7,7 @@ class ApplicationScheduledTask
end
def call
# override me
raise NotImplementedError
end
attr_reader :logger

عرض الملف

@@ -2,7 +2,7 @@
class ProcessMessageRetentionScheduledTask < ApplicationScheduledTask
def perform
def call
Server.all.each do |server|
if server.raw_message_retention_days
# If the server has a maximum number of retained raw messages, remove any that are older than this

عرض الملف

@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "socket"
require "rack/handler/webrick"
require "rackup/handler/webrick"
require "prometheus/client/formats/text"
class HealthServer
@@ -55,7 +55,7 @@ class HealthServer
port = ENV.fetch("HEALTH_SERVER_PORT", default_port)
bind_address = ENV.fetch("HEALTH_SERVER_BIND_ADDRESS", default_bind_address)
Rack::Handler::WEBrick.run(new(**options),
Rackup::Handler::WEBrick.run(new(**options),
Port: port,
BindAddress: bind_address,
AccessLog: [],
@@ -95,7 +95,7 @@ class HealthServer
Postal.logger.info "stopped health server", component: "health-server"
when /\AWEBrick [\d.]+/,
/\Aruby ([\d.]+)/,
/\ARack::Handler::WEBrick is mounted/,
/\ARackup::Handler::WEBrick is mounted/,
/\Aclose TCPSocket/,
/\Agoing to shutdown/
# Don't actually print routine messages to avoid too much

عرض الملف

@@ -38,7 +38,7 @@
= f.select :hold, [["Process all messages", false], ["Hold messages from this credential", true]], {}, :class => 'input input--select'
%p.fieldSet__text
You may wish to automatically hold all messages that are sent by this credential. This allows you to preview them
for they are delivered to their recipients. This is useful for credentials for development environments.
before they are delivered to their recipients. This is useful for credentials for development environments.
.fieldSetSubmit.buttonSet
= f.submit @credential.new_record? ? "Create credential" : "Save credential", :class => 'button button--positive js-form-submit'

عرض الملف

@@ -14,4 +14,4 @@
This means that we no longer store the raw data for this e-mail
or the e-mail didn't include a HTML part.
- else
%iframe{:width => "100%", :height => "100%", :src => html_raw_organization_server_message_path(organization, @server, @message.id)}
%iframe{:width => "100%", :height => "100%", :sandbox => "allow-popups allow-popups-to-escape-sandbox", :referrerpolicy => "no-referrer", :src => html_raw_organization_server_message_path(organization, @server, @message.id)}

عرض الملف

@@ -6,7 +6,7 @@ Rails.application.configure do
# In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
config.enable_reloading = true
# Do not eager load code on boot.
config.eager_load = false

عرض الملف

@@ -4,7 +4,7 @@ Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# Code is not reloaded between requests.
config.cache_classes = true
config.enable_reloading = false
# Eager load code on boot. This eager loads most of Rails and
# your application in memory, allowing both threaded web servers

عرض الملف

@@ -7,7 +7,7 @@ Rails.application.configure do
# test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
# and recreated between test runs. Don't rely on the data there!
config.cache_classes = true
config.enable_reloading = false
# Do not eager load code on boot. This avoids loading your whole application
# just for the purpose of running a single test. If you are using a tool that

عرض الملف

@@ -1,8 +1,8 @@
# frozen_string_literal: true
if Postal::Config.rails.secret_key
Rails.application.secrets.secret_key_base = Postal::Config.rails.secret_key
Rails.application.credentials.secret_key_base = Postal::Config.rails.secret_key
else
warn "No secret key was specified in the Postal config file. Using one for just this session"
Rails.application.secrets.secret_key_base = SecureRandom.hex(128)
Rails.application.credentials.secret_key_base = SecureRandom.hex(128)
end

عرض الملف

@@ -105,7 +105,7 @@ This document contains all the environment variables which are available for thi
| `OIDC_ISSUER` | String | The OIDC issuer URL | |
| `OIDC_IDENTIFIER` | String | The client ID for OIDC | |
| `OIDC_SECRET` | String | The client secret for OIDC | |
| `OIDC_SCOPES` | Array of strings | Scopes to request from the OIDC server. | openid,email |
| `OIDC_SCOPES` | Array of strings | Scopes to request from the OIDC server. | ["openid", "email"] |
| `OIDC_UID_FIELD` | String | The field to use to determine the user's UID | sub |
| `OIDC_EMAIL_ADDRESS_FIELD` | String | The field to use to determine the user's email address | email |
| `OIDC_NAME_FIELD` | String | The field to use to determine the user's name | name |

عرض الملف

@@ -239,7 +239,8 @@ oidc:
secret:
# Scopes to request from the OIDC server.
scopes:
- openid,email
- openid
- email
# The field to use to determine the user's UID
uid_field: sub
# The field to use to determine the user's email address

عرض الملف

@@ -1,4 +1,3 @@
version: "3"
services:
postal:
image: ${POSTAL_IMAGE}

عرض الملف

@@ -58,7 +58,7 @@ module Postal
unless silence_config_messages
warn "WARNING: Using legacy config file format. Upgrade your postal.yml to use"
warn "version 2 of the Postal configuration or configure using environment"
warn "variables. See https://postalserver.io/config-v2 for details."
warn "variables. See https://docs.postalserver.io/config-v2 for details."
end
sources << LegacyConfigSource.new(yaml)
when 2
@@ -145,7 +145,7 @@ module Postal
ActiveRecord::Base.connection_pool.disconnect!
config = ActiveRecord::Base.configurations
.configs_for(env_name: Rails.env)
.configs_for(env_name: Config.rails.environment)
.first
.configuration_hash
@@ -173,7 +173,7 @@ module Postal
private
def read_version_file(file)
path = Rails.root.join(file)
path = File.expand_path("../../../" + file, __FILE__)
return unless File.exist?(path)
value = File.read(path).strip

عرض الملف

@@ -550,7 +550,7 @@ module Postal
string :scopes do
description "Scopes to request from the OIDC server."
array
default "openid,email"
default ["openid", "email"]
end
string :uid_field do

عرض الملف

@@ -48,25 +48,11 @@ class TrackingMiddleware
Sentry.capture_exception(e) if defined?(Sentry)
end
source_image = request.params["src"]
case source_image
when nil
if request.params["src"].nil?
headers = {}
headers["Content-Type"] = "image/png"
headers["Content-Length"] = TRACKING_PIXEL.bytesize.to_s
[200, headers, [TRACKING_PIXEL]]
when /\Ahttps?:\/\//
response = Postal::HTTP.get(source_image, timeout: 3)
return [404, {}, ["Not found"]] unless response[:code] == 200
headers = {}
headers["Content-Type"] = response[:headers]["content-type"]&.first
headers["Last-Modified"] = response[:headers]["last-modified"]&.first
headers["Cache-Control"] = response[:headers]["cache-control"]&.first
headers["Etag"] = response[:headers]["etag"]&.first
headers["Content-Length"] = response[:body].bytesize.to_s
[200, headers, [response[:body]]]
else
[400, {}, ["Invalid/missing source image"]]
end

عرض الملف

@@ -1,5 +1,5 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require File.expand_path("../lib/postal/version", __dir__)
require File.expand_path("../lib/postal/config", __dir__)
puts Postal.version

عرض الملف

@@ -0,0 +1,37 @@
# frozen_string_literal: true
require "rails_helper"
RSpec.describe ApplicationHelper, type: :helper do
describe "#endpoint_options_for_select" do
let(:server) { create(:server) }
context "when an endpoint has HTML characters in its description" do
let(:payload) { %q(x'"><script>alert(1)</script>) }
before do
create(:http_endpoint, server: server, name: payload)
end
it "HTML-escapes the endpoint description in the option text" do
html = helper.endpoint_options_for_select(server)
# The raw payload must not appear verbatim — if it does, the browser
# will execute the <script> tag.
expect(html).not_to include("<script>alert(1)</script>")
# Escaped form should appear instead.
expect(html).to include("&lt;script&gt;alert(1)&lt;/script&gt;")
end
it "does not allow the payload to break out of the option tag" do
html = helper.endpoint_options_for_select(server)
# The ' and > characters in the payload must be escaped so they
# cannot close the opening <option value='...'> or terminate the
# element early.
expect(html).not_to match(/<option[^>]*>[^<]*<script/)
end
end
end
end

عرض الملف

@@ -0,0 +1,71 @@
# frozen_string_literal: true
require "rails_helper"
require "rack/test"
RSpec.describe TrackingMiddleware do
include Rack::Test::Methods
let(:inner_app) { ->(_env) { [200, {}, ["inner"]] } }
let(:app) { described_class.new(inner_app) }
let(:server) { create(:server) }
let(:message) do
MessageFactory.incoming(server) do |_msg, mail|
mail.html_part = Mail::Part.new do
content_type "text/html; charset=UTF-8"
body "<html><body>hi</body></html>"
end
end
end
def track_headers
{ "HTTP_X_POSTAL_TRACK_HOST" => "1" }
end
describe "GET /img/:server_token/:message_token (open tracking pixel)" do
before do
get "/img/#{server.token}/#{message.token}", {}, track_headers
end
it "returns the tracking pixel PNG" do
expect(last_response.status).to eq 200
expect(last_response.headers["Content-Type"]).to eq "image/png"
expect(last_response.body.bytesize).to be > 0
end
it "records a load for the message" do
# Re-fetch the message so loads are read fresh from the DB.
reloaded = server.message_db.message(message.id)
expect(reloaded.loads.size).to eq 1
end
end
describe "GET /img/:server_token/:message_token?src=<url> (image proxy)" do
let(:attacker_url) { "http://internal.example.com/secret" }
before do
stub_request(:get, attacker_url).to_return(status: 200, body: "internal-secret")
end
it "does not fetch the URL and returns 400" do
get "/img/#{server.token}/#{message.token}", { src: attacker_url }, track_headers
expect(last_response.status).to eq 400
expect(WebMock).not_to have_requested(:get, attacker_url)
end
it "does not fetch the URL even when the message token is invalid" do
get "/img/#{server.token}/nonexistent", { src: attacker_url }, track_headers
expect(WebMock).not_to have_requested(:get, attacker_url)
end
end
describe "when the track-host header is missing" do
it "passes the request through to the inner app untouched" do
get "/img/#{server.token}/#{message.token}"
expect(last_response.body).to eq "inner"
end
end
end

عرض الملف

@@ -120,6 +120,8 @@ describe Server do
end
describe "deletion" do
let(:server) { create(:server) }
it "removes the database" do
expect(server.message_db.provisioner).to receive(:drop).once
server.provision_database = true

عرض الملف

@@ -10,15 +10,13 @@ require "rspec/rails"
require "spec_helper"
require "factory_bot"
require "timecop"
require "database_cleaner"
require "webmock/rspec"
require "shoulda-matchers"
DatabaseCleaner.allow_remote_database_url = true
ActiveRecord::Base.logger = Logger.new("/dev/null")
Dir[File.expand_path("factories/*.rb", __dir__)].each { |f| require f }
Dir[File.expand_path("helpers/**/*.rb", __dir__)].each { |f| require f }
Dir[File.expand_path("helpers/**/*.rb", __dir__)].reject { |f| f.end_with?("_spec.rb") }.each { |f| require f }
ActionMailer::Base.delivery_method = :test

عرض الملف

@@ -0,0 +1,58 @@
# frozen_string_literal: true
require "rails_helper"
RSpec.describe "MessagesController", type: :request do
let(:user) { create(:user, admin: true) }
let(:organization) { create(:organization, owner: user) }
let(:server) { create(:server, organization: organization) }
before do
post "/login", params: { email_address: user.email_address, password: "passw0rd" }
end
describe "GET /org/:org/servers/:server/messages/:id/html_raw" do
let(:xss_payload) { %(<script>alert("XSS")</script>) }
let(:message) do
payload = xss_payload
MessageFactory.incoming(server) do |_msg, mail|
mail.html_part = Mail::Part.new do
content_type "text/html; charset=UTF-8"
body %(<html><body><p>hello</p>#{payload}</body></html>)
end
end
end
before do
get "/org/#{organization.permalink}/servers/#{server.permalink}/messages/#{message.id}/html_raw"
end
it "returns the stored email HTML" do
expect(response).to have_http_status(:ok)
expect(response.body).to include("hello")
end
it "serves a restrictive Content-Security-Policy that blocks scripts" do
csp = response.headers["Content-Security-Policy"]
expect(csp).to include("script-src 'none'")
expect(csp).to include("default-src 'none'")
expect(csp).to include("form-action 'none'")
expect(csp).to include("base-uri 'none'")
end
it "sets X-Content-Type-Options and Referrer-Policy on the response" do
expect(response.headers["X-Content-Type-Options"]).to eq "nosniff"
expect(response.headers["Referrer-Policy"]).to eq "no-referrer"
end
end
describe "messages/html view template" do
# We assert against the template source rather than rendering it in a
# request spec because the full application layout depends on the asset
# pipeline which is not configured in this test environment.
it "embeds the html_raw view inside a sandboxed iframe" do
template = Rails.root.join("app/views/messages/html.html.haml").read
expect(template).to match(/%iframe\{[^}]*:sandbox\s*=>/)
end
end
end

عرض الملف

@@ -0,0 +1,71 @@
# frozen_string_literal: true
require "rails_helper"
RSpec.describe "SessionsController", type: :request do
let(:user) { create(:user) }
describe "POST /login with return_to" do
def login_with(return_to:)
post "/login", params: {
email_address: user.email_address,
password: "passw0rd",
return_to: return_to
}
end
shared_examples "rejects unsafe return_to" do
it "does not redirect to the attacker-controlled location" do
login_with(return_to: unsafe_path)
expect(response).to have_http_status(:found)
# Whatever the fallback is, it must be same-origin: a Location that
# either omits a host or points at our own host. A browser must not
# end up at attacker.example.
location = response.location
expect(location).not_to include("attacker.example")
# Reject protocol-relative and absolute redirects entirely.
expect(location).not_to match(%r{\A//})
expect(location).not_to match(%r{\Ahttps?://attacker})
end
end
context "with a protocol-relative URL (//host)" do
let(:unsafe_path) { "//attacker.example/phish" }
include_examples "rejects unsafe return_to"
end
context "with a backslash-prefixed URL (/\\host)" do
let(:unsafe_path) { "/\\attacker.example/phish" }
include_examples "rejects unsafe return_to"
end
context "with an absolute http(s) URL" do
let(:unsafe_path) { "https://attacker.example/phish" }
include_examples "rejects unsafe return_to"
end
context "with a javascript: URL" do
let(:unsafe_path) { "javascript:alert(1)" }
include_examples "rejects unsafe return_to"
end
context "with a safe relative path" do
it "honours the return_to" do
login_with(return_to: "/org/acme/settings")
expect(response).to redirect_to("/org/acme/settings")
end
end
context "with no return_to" do
it "redirects to the default root" do
post "/login", params: {
email_address: user.email_address,
password: "passw0rd"
}
expect(response).to have_http_status(:found)
expect(response.location).not_to match(%r{\A//})
end
end
end
end

عرض الملف

@@ -59,8 +59,11 @@ RSpec.describe WebhookDeliveryService do
end
it "updates the last used at time on the webhook" do
frozen_time = Time.current.change(usec: 0)
Timecop.freeze(frozen_time) do
service.call
expect(webhook.reload.last_used_at).to be_within(1.second).of(Time.current)
expect(webhook.reload.last_used_at).to eq(frozen_time)
end
end
end