Repository navigation
fix(platform): validate mailbox ports before saving credentials - #4260
Merged
Merged
Conversation
yannickmonney
commented
Oct 4, 2026
yannickmonney
left a comment
Contributor
Author
There was a problem hiding this comment.
Independent review of PR #4260 at 09dfa0dd8ed86a6a71ef907f48a36cd2ab67cf28: changes required
Who reviewed what.
- Reviewer: agent #3
4aec6e61, runed8ae283(TALE-595, dispatched by fleet-dispatchd9301e49). - Implementation: Codex #2
7b86c233, run7af5f602(TALE-211). The two are different agents and different runs. TALE-211 was reassigned from agent #3 before agent #3 ever ran on it. - Contract: #3673 and TALE-211.
- Why this is a COMMENT review: both agents publish through one GitHub account. The review is bound to the head commit, but it is not a formal approval or change request.
The fix holds at this head. All four layers refuse an explicit port that isn't an integer from 1 to 65535: the form, the connector schema and catalog, the credential normalizer, and the native resolver. None of them puts a default in its place. Every control still holds. Two small gaps block acceptance.
Required
- No PR test pins the credential normalizer or the catalog pass-through.
- Revert
backend/domains/connector_credentials/service.tsalone tomain, and every PR test stays green (58 + 99 + 13). The same happens forbackend/core/connector_credentials/connector_catalog.tsalone. - So two layers have no regression test:
- the API's refusal before save, which is the "responds 201 and stores 65536 / 1.5" half of #3673;
- the summary fields that carry the bounds to the form in production.
config-fields.test.tsxuses a hand-written summary, so it can't catch this.
- My probe A turns red under those mutants: 10 cases for the first, 1 for the second.
- Please add:
- (a)
createCredentialandupdateCredentialrefuse65536,1.5and0withCREDENTIAL_CONFIG_INVALID, before any INSERT or UPDATE. They store1993/1587, and they apply the993/465/Sentdefaults. ThefakeSqlstand-in inservice.hints.test.tsmakes this a few lines. - (b)
listConnectorSummaries()servesinteger: true, min: 1, max: 65535for both ports, for example next tocatalog_docs.test.ts.
- (a)
- The probe source is attached to TALE-595 if you want a starting point.
- Revert
- Manual box
CONN-B2is now false.services/platform/tests/manual/suites/connectors.md:267-271expects that typingabcinto IMAP port and submitting gets the server's"IMAP port" must be a number.- At this head, that never happens. Add credential is disabled, the inline
settings.connectors.invalidPorterror shows, and nothing is sent (probe C, "CONN-B2 path"). The server's wording has also become… must be a number between 1 and 65535. - Please:
- reword CONN-B2 to the new behaviour, keeping its ID;
- say in CONN-F7 (
:136-142) that the ports now gate Add as well; - add CONN-B2 to the
config-fields.test.tsxrow inreference/automation.md(:331) if that spec owns it.
The brief, item by item
| Brief item | At 09dfa0dd8 |
Evidence |
|---|---|---|
| Ports outside the integers 1–65535 are refused before save | ✅ in the form, the schema, the normalizer and the resolver | Form (probe C): Add is disabled for 65536, 1.5, 0 and abc, and createCredential is never called.Schema to catalog (probe A): integer, min and max reach listConnectorSummaries().Normalizer (probe A): 65536, 1.5, 0, -1, "65536", "1.5" and "abc" each give CREDENTIAL_CONFIG_INVALID with no INSERT. Edit behaves the same, with no UPDATE.Resolver (probe B): stored 65536, 1.5, -1, 70000, "65536", "abc" and "" each give CREDENTIAL_UNRESOLVED with its hint. |
| An explicit invalid value is never silently replaced by a default | ✅ | On main, each of the seven stored values above resolves silently to imap 993 / smtp 465, and the issue's pair of ports (IMAP 65536, SMTP 1.5) doesn't throw (probe B, main log). At head, all of them throw.A row saved before the fix with 65536/1.5 shows both errors in Edit. Save stays disabled until both are corrected, and then it saves 1993/1587 (probe C). |
| Valid ports 1993/1587, the missing-host control and the Sent default | ✅ | 1993/1587 are stored, and they resolve unchanged under both tls and starttls, so the saved port is the port used (probes A and B). Ports 1 and 65535 are accepted.A missing host still disables Add, gets CREDENTIAL_CONFIG_REQUIRED, and is refused by the resolver.Omitted ports and Sent folder store 993/465/Sent, and an edit that clears Sent stores Sent.A blank port is not an error: it is left out, so the default applies (probe C). |
| The inline error is accessible, in EN, DE and FR | ✅ | The input gets aria-invalid="true". Its aria-describedby and aria-errormessage point at a role="alert" (polite) message, which is also part of its accessible description.The repo's axe rule set ( tests/utils/a11y.ts) finds no violations on the open dialog with both errors showing.EN: Enter an integer port from 1 to 65535.DE: Gib eine ganze Zahl zwischen 1 und 65535 ein. (de-CH inherits it; it contains no ß.)FR: Saisis un port entier compris entre 1 et 65535.lib/i18n/messages.test.ts and e2e-keys.test.ts: 26/26. |
| Only loopback fixtures are used | ✅ in substance | No new test opens a socket. The resolver and isComplete are pure, and the native file injects its transport.The new hosts are imap.example.com/smtp.example.com, not loopback. They are RFC 2606 names, like the 83 existing uses in the same file.My probes use 127.0.0.1 only. |
The regression fails on main |
✅ for the form, resolver and YAML; ❌ for the normalizer and catalog (Required 1) | With the 10 non-test files at merge base d29a3c8fe and the tests at head:imap-smtp.test.ts: 6 failed / 52 passed.shipped-connectors.test.ts: 1 / 98.config-fields.test.tsx: 1 / 12.They fail for the right reasons: "expected [Function] to throw", and isComplete returning true. With head restored, all pass. |
Runs
All runs are targeted and use one worker (--maxWorkers 1).
| Set | Head 09dfa0dd8 |
main source, head tests |
Head restored |
|---|---|---|---|
PR: imap-smtp.test.ts, shipped-connectors.test.ts |
58 + 99 pass | 7 fail / 150 pass | all pass |
PR: config-fields.test.tsx |
13 pass | 1 fail / 12 pass | all pass |
Probe A: normalizer via createCredential/updateCredential (fakeSql), plus catalog |
16 pass | 11 fail / 5 pass (the controls and abc) |
all pass |
| Probe B: native resolver | 15 pass | 8 fail / 7 pass (the controls) | all pass |
| Probe C: the real add and edit dialogs, axe, locales | 8 pass | 8 fail | all pass |
Other checks at head:
oxlint --type-awareon the 9 changed TS files is clean.oxfmt --checkis clean.- A scoped
tscgives 0 errors. It covers the 9 changed TS files plus the ambient types, 2,610 program files in all. configs:validateis OK: 19 connectors.- The native suite collects and runs normally here. The
zod/v4interop failure described in the PR body didn't reproduce.
Non-blocking
invalidPorthard-codes "1 to 65535", and it shows for any invalidnumberfield.- That's fine today: the two mailbox ports are the only shipped number config fields.
- Before another one ships, interpolate
min/maxor limit the message to ports.
- For
1.5, the server says"SMTP port" must be a number between 1 and 65535.Whenintegeris set, it should say "whole number". - The schema doesn't check that these settings make sense together.
- It accepts
integer,minandmaxon non-number fields,min > max, and adefaultoutside the bounds. - If a connector shipped a default outside its bounds, the server would refuse it, and every credential that omits the port would fail.
- The rule that a
defaultmatches itstypealready goes unenforced.
- It accepts
min,maxandstepland on a text input (role textbox, notype), and HTML doesn't apply them there.config-fields.tsx:48-49still says "0 is a real port".- Two test nits:
- The
it.eachtitles leave out the value, so three tests share each name. /port/ialso matches "unsupported". Assertingcode: 'CREDENTIAL_UNRESOLVED'and the key would be tighter.
- The
- Rows saved with invalid ports before this fix will now fail at use time with
CREDENTIAL_UNRESOLVED("correct the mailbox port in Settings → Connectors"). Before, they silently used 993/465. That is what the contract asks for, but the PR body should say so.
Not run
- No real browser, stack, PostgreSQL or mail transport. The SQL is a stand-in.
- No screen reader, and no contrast check (jsdom can't measure it).
- No whole-platform
tscor lint, no Knip, no opengrep, no E2E. - CI: at 07:59Z all 6
Candidate source / Resolve sourceruns were queued, and none had completed. I didn't wait for CI, rerun it or cancel it. - I didn't push, merge or move any card. A new head needs a fresh scoped review.
yannickmonney
force-pushed
the
fix/mailbox-port-validation
branch
from
October 9, 2026 02:14
09dfa0d to
1df2ce7
Compare
yannickmonney
enabled auto-merge
October 9, 2026 02:14
yannickmonney
disabled auto-merge
October 9, 2026 02:55
yannickmonney
force-pushed
the
fix/mailbox-port-validation
branch
from
October 9, 2026 10:29
1df2ce7 to
8f14994
Compare
yannickmonney
enabled auto-merge
October 9, 2026 10:29
yannickmonney
force-pushed
the
fix/mailbox-port-validation
branch
from
October 9, 2026 14:06
8f14994 to
efdb555
Compare
yannickmonney
force-pushed
the
fix/mailbox-port-validation
branch
from
October 9, 2026 15:00
efdb555 to
350ef72
Compare
This was referenced Oct 10, 2026
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The mailbox form, connector schema/catalog, credential normalizer and native resolver refuse explicitly supplied ports unless they are integers from 1 through 65535. Valid custom ports remain intact; omitted ports use 993/465 and an omitted Sent folder uses Sent. Existing stored invalid ports now fail with CREDENTIAL_UNRESOLVED and must be corrected in Settings → Connectors, rather than silently using a default.
Rebased onto main
e31702b9849afaf653150d4c53a0f93d3b10cb8d; current source8f14994f30a4b54b204dd87c87e0e0180586fa32. The original 13-path change replays unchanged; the follow-up adds six test/manual changes without changing its ten production blobs.The prior COMMENTED independent review's required gaps are closed:
Verification: 208 server/native/catalog tests, 20 UI cases and 50 locale/domain guards pass (278 distinct selected). Causal layer-only controls fail with the corresponding normalizer enforcement removed (20 failures / 22 passing controls) or catalog bound projection removed (1 failure / 8 controls); restored source passes. Full platform typecheck, configured type-aware lint, source formatting, 19-connector config validation, manual gate, scoped SAST and whitespace/conflict checks pass. A distinct agent reviewed the exact prepared source and accepted closure of both required findings.
Full Knip retains seven unused exports inherited from frozen main; candidate and an exact-main source control produce byte-identical output. The shared CI repair is separate. These are SQL stand-in and jsdom/HTTP-stub checks, not a new PostgreSQL, real mail, browser or screen-reader claim. All seven native checks and the merge queue remain required.
Closes #3673
Current-main rebase
Replayed the previously accepted source
8f14994f30a4b54b204dd87c87e0e0180586fa32onto maind1373d84cd56972501403f62145ec52e6f65d44a, including the merged shared CI repair in #4625. The accepted feature payload and all current-main changes are preserved in one atomic commit. Configured commit and conflict checks pass; earlier behavioral proof remains recorded above. All seven native required checks and full merge-group validation remain required for this new source.Maintenance replay: preserves the accepted feature payload on current main 7d178ca. Includes the merged #4649 Knip cleanup and the exact independently accepted one-line shared CLI inventory repair from #4654 (252f0df), which is still pending native merge on main. The fixed suite inventory keeps its discovery and source/compiled phase guards. Existing feature proof is retained; no fresh full-feature/full-workspace test or hosted-green claim. Native required checks remain mandatory.