Skip to content

🐛 bug: BasicAuth verifier for unknown users#4245

Merged
ReneWerner87 merged 8 commits into
mainfrom
update-default-authorizer-for-password-verification
Apr 29, 2026
Merged

🐛 bug: BasicAuth verifier for unknown users#4245
ReneWerner87 merged 8 commits into
mainfrom
update-default-authorizer-for-password-verification

Conversation

@gaby
Copy link
Copy Markdown
Member

@gaby gaby commented Apr 29, 2026

Motivation

  • Prevent timing-based user enumeration by ensuring password verification work is performed even when the username is not found.
  • Keep authorization semantics unchanged so unknown users remain unauthorized while reducing observable timing differences.

Description

  • Capture a dummyVerify function from the first parsed hashed password during verifiers construction to use for missing users.
  • Update the Authorizer to call dummyVerify when a username is not present, and otherwise call the real verifier, while returning ok && res to preserve outcome.
  • Add a hasDummy flag and safe initialization to avoid nil verifier invocation and rely on parseHashedPassword results.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 29, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

When cfg.Authorizer is nil, the middleware deterministically builds per-user password verifier functions from stored hashes, selects a dummy verifier based on the strongest configured hash (or fixed SHA‑512 fallback), and the default authorizer always invokes a verifier for both known and unknown usernames, returning ok && res.

Changes

Cohort / File(s) Summary
Basic Auth Configuration
middleware/basicauth/config.go
Added buildVerifiers to parse stored password hashes and produce per-user passwordVerifiers; deterministically sort usernames; derive a dummyVerify from the strongest parsed algorithm/cost (bcrypt cost extraction, SHA‑512, SHA‑256); updated default cfg.Authorizer to always call a verifier and return ok && res.
Verifier Tests
middleware/basicauth/basicauth_test.go
Added Test_buildVerifiers with subtests validating verifier list construction for multiple users, dummy selection matching the strongest configured verifier, and deterministic fallback dummyVerify when no users provided.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant BasicAuthMiddleware as Middleware
    participant Verifier as VerifierStore
    participant UserDB as ConfiguredHashes

    Client->>Middleware: send request with Authorization header
    Middleware->>UserDB: lookup username -> hashed entry?
    alt user found
        Middleware->>Verifier: select user verifier for stored hash
    else user not found
        Middleware->>Verifier: use dummyVerify (selected by strongest configured hash)
    end
    Verifier->>Verifier: verify(password)
    Verifier-->>Middleware: result (true/false)
    Middleware-->>Client: allow if ok && result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • sixcolors
  • efectn
  • ReneWerner87

Poem

"I nibble through salts and hashes bright,
sorting names by moonlit byte.
A dummy guard I choose with care,
so strangers face a faithful snare.
Hop, verify, then dream of light." 🐇✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description includes motivation, description of changes, and implementation details, but lacks completion of the required template sections like Changes introduced checklist, Type of change, and Checklist items. Complete the PR description template by filling out the Changes introduced section, Type of change section, and the Checklist section to meet repository standards.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title references a bug fix for BasicAuth verifier handling of unknown users, which aligns with the core security improvement described in the PR objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch update-default-authorizer-for-password-verification

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ReneWerner87 ReneWerner87 added this to v3 Apr 29, 2026
@ReneWerner87 ReneWerner87 added this to the v3 milestone Apr 29, 2026
@gaby gaby changed the title basicauth: call a dummy verifier for unknown users to reduce timing differences 🐛 bug: Improve BasicAuth verifier for unknown users Apr 29, 2026
@gaby gaby moved this to In Progress in v3 Apr 29, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 29, 2026

Codecov Report

❌ Patch coverage is 84.44444% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.18%. Comparing base (f6ecd99) to head (e19d29b).
⚠️ Report is 21 commits behind head on main.

Files with missing lines Patch % Lines
middleware/basicauth/config.go 84.44% 4 Missing and 3 partials ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##             main    #4245    +/-   ##
========================================
  Coverage   91.17%   91.18%            
========================================
  Files         123      126     +3     
  Lines       12084    12341   +257     
========================================
+ Hits        11018    11253   +235     
- Misses        668      683    +15     
- Partials      398      405     +7     
Flag Coverage Δ
unittests 91.18% <84.44%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces timing attack protection in the basic authentication middleware by ensuring a password verification step is always performed, even if the user does not exist. The review feedback suggests simplifying the logic for initializing the dummy verifier by removing the redundant hasDummy flag and streamlining the conditional check within the authorizer function to improve code clarity while maintaining the security fix.

Comment thread middleware/basicauth/config.go Outdated
Comment thread middleware/basicauth/config.go
@gaby gaby changed the title 🐛 bug: Improve BasicAuth verifier for unknown users 🐛 bug: BasicAuth verifier for unknown users Apr 29, 2026
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.50.

Benchmark suite Current: df83d1f Previous: f6ecd99 Ratio
Benchmark_Compress/Zstd (github.com/gofiber/fiber/v3/middleware/compress) - B/op 1 B/op 0 B/op +∞

This comment was automatically generated by workflow using github-action-benchmark.

@gaby gaby marked this pull request as ready for review April 29, 2026 05:04
Copilot AI review requested due to automatic review settings April 29, 2026 05:04
@gaby gaby requested a review from a team as a code owner April 29, 2026 05:04
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to reduce timing-based user enumeration in the BasicAuth middleware by ensuring password verification work is performed even when the username is not found, while keeping authorization outcomes unchanged.

Changes:

  • Capture a “dummy” password verifier during cfg.Users parsing to use for unknown usernames.
  • Update the default Authorizer to always run a verifier (real or dummy) and return ok && res.
  • Add initialization logic (hasDummy) to avoid calling a nil verifier.

Comment thread middleware/basicauth/config.go Outdated
Comment on lines +128 to +139
dummyVerify := func(string) bool { return false }
hasDummy := false
for u, hpw := range cfg.Users {
v, err := parseHashedPassword(hpw)
if err != nil {
panic(err)
}
verifiers[u] = v
if !hasDummy {
dummyVerify = v
hasDummy = true
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was already addressed in a prior commit via sort.Strings(keys) — the dummy is now selected deterministically by iterating sorted keys and picking the strongest verifier. Additionally in e19d29b, the hasDummy flag was removed since the zero-value comparison handles the first-entry case naturally.

Comment thread middleware/basicauth/config.go Outdated
Comment on lines +128 to +139
dummyVerify := func(string) bool { return false }
hasDummy := false
for u, hpw := range cfg.Users {
v, err := parseHashedPassword(hpw)
if err != nil {
panic(err)
}
verifiers[u] = v
if !hasDummy {
dummyVerify = v
hasDummy = true
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was already addressed in a prior commit — fallbackDummyVerify performs a SHA-512 hash + subtle.ConstantTimeCompare for the empty-users case. In e19d29b, the preimage value ("fiber-basicauth-dummy") is now documented at the fallbackDummySHA512 declaration site.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@middleware/basicauth/config.go`:
- Around line 128-148: The current dummyVerify approach only exercises one
verifier and leaks timing differences across hash types; replace it by
collecting all parsed verifiers (from parseHashedPassword while iterating
cfg.Users into verifiers map) and in cfg.Authorizer always run every verifier
against the supplied password (or at least every verifier type/cost class) and
combine results so the function's runtime is independent of whether the username
exists: use the real user's verifier result as the authoritative boolean but
still execute the remaining verifiers to normalize cost; reference
parseHashedPassword, verifiers, dummyVerify (remove/repurpose), cfg.Authorizer
and cfg.Users when making this change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0e3eb3bc-4692-4947-aa0c-1305ab70a95d

📥 Commits

Reviewing files that changed from the base of the PR and between f6ecd99 and df83d1f.

📒 Files selected for processing (1)
  • middleware/basicauth/config.go

Comment thread middleware/basicauth/config.go Outdated
@gaby
Copy link
Copy Markdown
Member Author

gaby commented Apr 29, 2026

@copilot apply changes based on the comments in this thread

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
middleware/basicauth/config.go (1)

132-143: ⚠️ Potential issue | 🟠 Major

Single dummy verifier still leaks mixed-hash timing differences.

Line 142 still runs exactly one verifier. In a config that mixes bcrypt, SHA-512, and SHA-256 credentials, unknown usernames take the selected dummy path while known users take their account-specific verifier, so cheaper hash types remain distinguishable from “missing user”. Please normalize work across all configured verifier classes on every request, and keep only the matched user’s result authoritative.

Also applies to: 173-200

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@middleware/basicauth/config.go` around lines 132 - 143, The current
cfg.Authorizer closure uses a single dummyVerify and runs only one verifier per
request, leaking timing differences between missing users and users with cheaper
hash types; modify the Authorizer (the anonymous func assigned to cfg.Authorizer
that uses verifiers, dummyVerify and the verifiers map returned by
buildVerifiers) to always execute the full set of verifier functions for every
configured hash class on every request (e.g., iterate all entries/types or a
canonical list of verifier functions) and aggregate their work/latency, but only
use the matched user's verifier result as the authoritative authentication
outcome; ensure dummy stubs for each hash type are invoked when the username is
unknown so execution time is constant across missing vs existing users (apply
the same change pattern to the other authorizer block referenced in the file).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@middleware/basicauth/config.go`:
- Around line 132-143: The current cfg.Authorizer closure uses a single
dummyVerify and runs only one verifier per request, leaking timing differences
between missing users and users with cheaper hash types; modify the Authorizer
(the anonymous func assigned to cfg.Authorizer that uses verifiers, dummyVerify
and the verifiers map returned by buildVerifiers) to always execute the full set
of verifier functions for every configured hash class on every request (e.g.,
iterate all entries/types or a canonical list of verifier functions) and
aggregate their work/latency, but only use the matched user's verifier result as
the authoritative authentication outcome; ensure dummy stubs for each hash type
are invoked when the username is unknown so execution time is constant across
missing vs existing users (apply the same change pattern to the other authorizer
block referenced in the file).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0e6b3f63-38ce-41d4-a6d6-74e1d260b53d

📥 Commits

Reviewing files that changed from the base of the PR and between df83d1f and 07e867e.

📒 Files selected for processing (2)
  • middleware/basicauth/basicauth_test.go
  • middleware/basicauth/config.go

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
middleware/basicauth/config.go (1)

139-150: ⚠️ Potential issue | 🟠 Major

Single-verifier execution still leaves a timing side channel.

This path runs exactly one verifier per request: dummyVerify for unknown users, or the matched verifier for known users. In mixed configs, existing SHA-256/SHA-512 users or lower-cost bcrypt users remain measurably different from the unknown-user path, so username enumeration is still possible. Normalize work across all configured verifier classes/costs for every request, and use only the matched user’s result as the auth decision.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@middleware/basicauth/config.go` around lines 139 - 150, The authorizer
currently runs only one verifier (dummyVerify or the matched verifier) leaving
timing differences; modify cfg.Authorizer (the closure using verifiers,
dummyVerify, v(pass)) to call every verifier function on the supplied password
on every request (iterate the verifiers map and also call dummyVerify) to
equalize cost, but only use the matched user's verifier result as the
authentication decision (capture the result of verifiers[user] when ok, or
dummyVerify when user missing) and return ok && matchedResult; ensure other
verifier results are discarded but executed to normalize timing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@middleware/basicauth/config.go`:
- Around line 139-150: The authorizer currently runs only one verifier
(dummyVerify or the matched verifier) leaving timing differences; modify
cfg.Authorizer (the closure using verifiers, dummyVerify, v(pass)) to call every
verifier function on the supplied password on every request (iterate the
verifiers map and also call dummyVerify) to equalize cost, but only use the
matched user's verifier result as the authentication decision (capture the
result of verifiers[user] when ok, or dummyVerify when user missing) and return
ok && matchedResult; ensure other verifier results are discarded but executed to
normalize timing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 58414d7a-da7e-457b-82ad-89e0e1a7803f

📥 Commits

Reviewing files that changed from the base of the PR and between 07e867e and f9eee36.

📒 Files selected for processing (2)
  • middleware/basicauth/basicauth_test.go
  • middleware/basicauth/config.go

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
middleware/basicauth/config.go (1)

148-159: ⚠️ Potential issue | 🟠 Major

Single dummy verifier still leaves mixed-hash deployments enumerable.

This still executes only one verifier per request. In a mixed setup, unknown usernames will match the timing of the selected dummy, but they remain distinguishable from real accounts backed by cheaper verifiers or lower-cost bcrypt hashes. To actually close the username-existence side channel, the default path needs to run a normalized verifier workload for every request and use only the matched user’s result as the auth decision.

Also applies to: 189-217

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@middleware/basicauth/config.go` around lines 148 - 159, The current
cfg.Authorizer uses a single dummyVerify and then returns ok && v(pass), which
still leaks username existence via timing because only one verifier is executed
or its result short-circuited; change cfg.Authorizer so it always executes a
normalized verifier workload for every request (i.e., always call a verifier
workload for the supplied password), but only use the matched user's verifier
result as the final auth decision: lookup verify := verifiers[user], set
fallbackVerify := dummyVerify and always call both (or run the verified workload
then combine) such that timing is equalized, then return
verifyResultOnlyIfUserExists (use verify result when ok is true, otherwise
discard verify result but ensure dummyVerify was executed to match timing).
Ensure this logic replaces the current use of v(pass) and the ok && res return
so unknown usernames run the same amount of work as real ones.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@middleware/basicauth/config.go`:
- Around line 148-159: The current cfg.Authorizer uses a single dummyVerify and
then returns ok && v(pass), which still leaks username existence via timing
because only one verifier is executed or its result short-circuited; change
cfg.Authorizer so it always executes a normalized verifier workload for every
request (i.e., always call a verifier workload for the supplied password), but
only use the matched user's verifier result as the final auth decision: lookup
verify := verifiers[user], set fallbackVerify := dummyVerify and always call
both (or run the verified workload then combine) such that timing is equalized,
then return verifyResultOnlyIfUserExists (use verify result when ok is true,
otherwise discard verify result but ensure dummyVerify was executed to match
timing). Ensure this logic replaces the current use of v(pass) and the ok && res
return so unknown usernames run the same amount of work as real ones.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 32cebc50-d43c-4c03-8203-f7f9a7f8e7a2

📥 Commits

Reviewing files that changed from the base of the PR and between f9eee36 and 389127f.

📒 Files selected for processing (2)
  • middleware/basicauth/basicauth_test.go
  • middleware/basicauth/config.go

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 29, 2026

@copilot apply changes based on the comments in this thread

Addressed in 7963956. The BasicAuth dummy verifier is now selected deterministically from the strongest configured hash, empty-user configs use a fixed-work fallback verifier, and tests cover both behaviors.

Copilot finished work on behalf of gaby April 29, 2026 06:13
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
middleware/basicauth/config.go (1)

152-160: ⚠️ Potential issue | 🟠 Major

Single dummy verification still leaks timing in mixed-hash user sets.

Line 152–160 normalizes unknown users to one verifier only. If configured users mix bcrypt/SHA-512/SHA-256 (or bcrypt costs), timings can still distinguish unknown vs some existing usernames. Normalize against all configured verifier classes (or all verifiers) per request, while keeping the matched user result authoritative.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@middleware/basicauth/config.go` around lines 152 - 160, The current
cfg.Authorizer uses a single dummyVerify for unknown users which can leak timing
when verifiers vary; modify cfg.Authorizer to call all configured verifiers (the
functions in the verifiers map) for every request to normalize timing, but still
return the authoritative result of the matched user: locate cfg.Authorizer,
remove the single dummyVerify substitution, iterate over verifiers (or verifier
classes) and invoke each one with the provided pass to burn equal time, capture
the matched user's verifier result separately (if user exists in verifiers use
that result), and finally return ok && matchedResult so timing is normalized
while the matched user's boolean determines acceptance.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@middleware/basicauth/config.go`:
- Around line 152-160: The current cfg.Authorizer uses a single dummyVerify for
unknown users which can leak timing when verifiers vary; modify cfg.Authorizer
to call all configured verifiers (the functions in the verifiers map) for every
request to normalize timing, but still return the authoritative result of the
matched user: locate cfg.Authorizer, remove the single dummyVerify substitution,
iterate over verifiers (or verifier classes) and invoke each one with the
provided pass to burn equal time, capture the matched user's verifier result
separately (if user exists in verifiers use that result), and finally return ok
&& matchedResult so timing is normalized while the matched user's boolean
determines acceptance.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 057d2466-5f04-4b8e-addf-8f0dbb1208c8

📥 Commits

Reviewing files that changed from the base of the PR and between 389127f and 7963956.

📒 Files selected for processing (2)
  • middleware/basicauth/basicauth_test.go
  • middleware/basicauth/config.go

- Remove redundant hasDummy flag (zero-value verifierStrength always
  loses to real values)
- Simplify Authorizer closure by directly reassigning verify variable
- Document fallbackDummySHA512 preimage value at declaration site
- Replace obfuscated hex byte array with string literal in test
- Document mixed-hash timing limitation on buildVerifiers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Member

@ReneWerner87 ReneWerner87 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

Pushed e19d29b addressing the open review comments. Changes:

Resolved:

  • Removed hasDummy flag (Gemini) — zero-value verifierStrength{} naturally loses to any real strength, making the flag redundant
  • Simplified Authorizer closure (Gemini) — eliminated intermediate v variable, directly reassign verify when user not found
  • Deterministic dummy selection (Copilot) — was already fixed via sort.Strings(keys) in prior commits
  • Empty users fallback (Copilot) — was already fixed via fallbackDummyVerify with SHA-512

Additionally:

  • Documented fallbackDummySHA512 preimage value ("fiber-basicauth-dummy") at declaration site
  • Replaced obfuscated hex byte array in test with plain string literal for readability
  • Added doc comment on buildVerifiers explicitly documenting the mixed-hash timing limitation as an accepted trade-off

Acknowledged limitation (CodeRabbit):
The single-dummy-verifier approach doesn't fully normalize timing in mixed-hash deployments. This is now documented. Running all verifier types per request would be prohibitively expensive and impractical for a middleware default.

@ReneWerner87 ReneWerner87 merged commit c7ac00e into main Apr 29, 2026
19 of 20 checks passed
@ReneWerner87 ReneWerner87 deleted the update-default-authorizer-for-password-verification branch April 29, 2026 09:32
@github-project-automation github-project-automation Bot moved this from In Progress to Done in v3 Apr 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants