Skip to content

Commit ee81ed2

Browse files
authored
Federation domain validation (#1393)
* move domain validation to FederationServer.createForDomain and constructor * add tests for domain validation
1 parent 7ad2b0a commit ee81ed2

4 files changed

Lines changed: 31 additions & 12 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ A breaking change will get clearly marked in this log.
1515
* Fixed bigint-to-U32/I32 conversion in `Spec` using `Number(val)` instead of `val as number` (a no-op for bigints) ([#1373](https://github.com/stellar/js-stellar-sdk/pull/1373)).
1616
* Fixed missing template literal `$` in two `Spec` error messages that were not interpolated ([#1373](https://github.com/stellar/js-stellar-sdk/pull/1373)).
1717
* WASM custom section parser: when a section was skipped (invalid name length), the offset was not advanced, causing an infinite loop or incorrect parsing of subsequent sections ([#1373](https://github.com/stellar/js-stellar-sdk/pull/1373)).
18-
* `FederationServer.resolve` now validates domains per RFC 1035, rejecting malformed domains. Port numbers are also accepted in the domain ([#1373](https://github.com/stellar/js-stellar-sdk/pull/1373)).
18+
* `FederationServer.createForDomain` and the `FederationServer` constructor now validate domains per RFC 1035, rejecting malformed domains; port numbers are also accepted. This may be breaking for callers that previously omitted `domain` or passed an invalid domain ([#1373](https://github.com/stellar/js-stellar-sdk/pull/1373)).
1919
* `FederationServer` URL mutation: `resolveAddress`, `resolveAccountId`, and `resolveTransactionId` mutated the shared `serverURL` by appending query params on each call. Fixed by cloning the URL before modifying ([#1373](https://github.com/stellar/js-stellar-sdk/pull/1373)).
2020
* `CallBuilder.stream()` URL mutation: `stream()` mutated the shared `this.url` by adding query params, corrupting the builder for subsequent calls. Fixed by cloning the URL ([#1373](https://github.com/stellar/js-stellar-sdk/pull/1373)).
2121
* `AssembledTransaction` restore path: when `buildWithOp` was used and automatic state restoration was needed, the rebuild incorrectly reconstructed the operation via `contract.call()` instead of reusing the original operation ([#1373](https://github.com/stellar/js-stellar-sdk/pull/1373)).

src/federation/server.ts

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Resolver } from "../stellartoml";
77

88
import { Api } from "./api";
99
import { httpClient } from "../http-client";
10+
import { validateDomain } from "./utils";
1011

1112
/** @module Federation */
1213

@@ -91,16 +92,6 @@ export class FederationServer {
9192
return Promise.reject(new Error("Invalid Stellar address"));
9293
}
9394

94-
// Validate domain per RFC 1035 (as required by SEP-0002): each dot-separated
95-
// label must start with a letter, end with a letter or digit, and contain only
96-
// letters, digits, or hyphens.
97-
if (
98-
!/^(?:[A-Za-z](?:[A-Za-z0-9-]*[A-Za-z0-9])?\.)*[A-Za-z](?:[A-Za-z0-9-]*[A-Za-z0-9])?(?::\d+)?$/.test(
99-
domain,
100-
)
101-
) {
102-
return Promise.reject(new Error("Invalid domain in Stellar address"));
103-
}
10495
const federationServer = await FederationServer.createForDomain(
10596
domain,
10697
opts,
@@ -135,6 +126,7 @@ export class FederationServer {
135126
domain: string,
136127
opts: Api.Options = {},
137128
): Promise<FederationServer> {
129+
validateDomain(domain);
138130
const tomlObject = await Resolver.resolve(domain, opts);
139131
if (!tomlObject.FEDERATION_SERVER) {
140132
return Promise.reject(
@@ -149,9 +141,9 @@ export class FederationServer {
149141
domain: string,
150142
opts: Api.Options = {},
151143
) {
152-
// TODO `domain` regexp
153144
this.serverURL = URI(serverURL);
154145
this.domain = domain;
146+
validateDomain(domain);
155147

156148
const allowHttp =
157149
typeof opts.allowHttp === "undefined"

src/federation/utils.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export function validateDomain(domain: string): void {
2+
// Validate domain per RFC 1035 with an optional port (as required by SEP-0002): each dot-separated
3+
// label must start with a letter, end with a letter or digit, and contain only
4+
// letters, digits, or hyphens.
5+
if (
6+
!/^(?:[A-Za-z](?:[A-Za-z0-9-]*[A-Za-z0-9])?\.)*[A-Za-z](?:[A-Za-z0-9-]*[A-Za-z0-9])?(?::\d+)?$/.test(
7+
domain,
8+
)
9+
) {
10+
throw new Error(
11+
"The provided domain is invalid. Ensure that the domain adheres to RFC 1035",
12+
);
13+
}
14+
}

test/unit/federation_server.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ describe("federation-server.js tests", () => {
2727
).toThrow(/Cannot connect to insecure federation server/);
2828
});
2929

30+
it("throws error for invalid domain", () => {
31+
expect(
32+
() => new Server("https://acme.com:1337/federation", "-stellar.org"),
33+
).toThrow(/The provided domain is invalid/);
34+
});
35+
3036
it("allow insecure server when opts.allowHttp flag is set", () => {
3137
expect(
3238
() =>
@@ -170,6 +176,13 @@ FEDERATION_SERVER="https://api.stellar.org/federation"
170176
expect(federationServer["domain"]).toEqual("acme.com");
171177
});
172178

179+
it("fails for invalid domain before requesting stellar.toml", async () => {
180+
await expect(Server.createForDomain("-acme.com")).rejects.toThrow(
181+
/The provided domain is invalid/,
182+
);
183+
expect(mockHttpClient).not.toHaveBeenCalled();
184+
});
185+
173186
it("fails when stellar.toml does not contain federation server info", async () => {
174187
mockHttpClient.mockImplementation((url: string) => {
175188
if (url.includes("https://acme.com/.well-known/stellar.toml")) {

0 commit comments

Comments
 (0)