





















@@ -902,6 +902,9 @@ describe("trusted-proxy auth", () => {
902902function authorizeLocalDirect(options?: {
903903token?: string;
904904connectToken?: string;
905+password?: string;
906+connectPassword?: string;
907+rateLimiter?: AuthRateLimiter;
905908trustedProxy?: GatewayConnectInput["auth"]["trustedProxy"];
906909trustedProxies?: string[];
907910}) {
@@ -913,8 +916,13 @@ describe("trusted-proxy auth", () => {
913916 ? { trustedProxy: options?.trustedProxy }
914917 : { trustedProxy: trustedProxyConfig }),
915918token: options?.token,
919+password: options?.password, // pragma: allowlist secret
916920},
917-connectAuth: options?.connectToken ? { token: options.connectToken } : null,
921+connectAuth:
922+options?.connectToken || options?.connectPassword
923+ ? { token: options.connectToken, password: options.connectPassword }
924+ : null,
925+rateLimiter: options?.rateLimiter,
918926trustedProxies: options?.trustedProxies ?? ["127.0.0.1"],
919927req: {
920928socket: { remoteAddress: "127.0.0.1" },
@@ -956,6 +964,67 @@ describe("trusted-proxy auth", () => {
956964expect(res.reason).toBe("trusted_proxy_loopback_source");
957965});
958966967+it("accepts local-direct password fallback when trusted-proxy auth fails", async () => {
968+const limiter = createLimiterSpy();
969+const res = await authorizeLocalDirect({
970+password: "local-password", // pragma: allowlist secret
971+connectPassword: "local-password", // pragma: allowlist secret
972+rateLimiter: limiter,
973+});
974+975+expect(res).toEqual({ ok: true, method: "password" });
976+expect(limiter.check).toHaveBeenCalledWith("127.0.0.1", "shared-secret");
977+expect(limiter.reset).toHaveBeenCalledWith("127.0.0.1", "shared-secret");
978+expect(limiter.recordFailure).not.toHaveBeenCalled();
979+});
980+981+it("rejects wrong local-direct password fallback and records the failure", async () => {
982+const limiter = createLimiterSpy();
983+const res = await authorizeLocalDirect({
984+password: "local-password", // pragma: allowlist secret
985+connectPassword: "wrong-password", // pragma: allowlist secret
986+rateLimiter: limiter,
987+});
988+989+expect(res).toEqual({ ok: false, reason: "password_mismatch" });
990+expect(limiter.check).toHaveBeenCalledWith("127.0.0.1", "shared-secret");
991+expect(limiter.recordFailure).toHaveBeenCalledWith("127.0.0.1", "shared-secret");
992+expect(limiter.reset).not.toHaveBeenCalled();
993+});
994+995+it("enforces rate-limit lockout before local-direct password fallback", async () => {
996+const limiter = createLimiterSpy();
997+limiter.check.mockReturnValueOnce({
998+allowed: false,
999+remaining: 0,
1000+retryAfterMs: 2500,
1001+});
1002+1003+const res = await authorizeLocalDirect({
1004+password: "local-password", // pragma: allowlist secret
1005+connectPassword: "local-password", // pragma: allowlist secret
1006+rateLimiter: limiter,
1007+});
1008+1009+expect(res).toEqual({
1010+ok: false,
1011+reason: "rate_limited",
1012+rateLimited: true,
1013+retryAfterMs: 2500,
1014+});
1015+expect(limiter.recordFailure).not.toHaveBeenCalled();
1016+expect(limiter.reset).not.toHaveBeenCalled();
1017+});
1018+1019+it("keeps local-direct trusted-proxy on proxy failure when no password is supplied", async () => {
1020+const res = await authorizeLocalDirect({
1021+password: "local-password", // pragma: allowlist secret
1022+});
1023+1024+expect(res.ok).toBe(false);
1025+expect(res.reason).toBe("trusted_proxy_loopback_source");
1026+});
1027+9591028it("rejects trusted-proxy identity headers from loopback sources", async () => {
9601029const res = await authorizeGatewayConnect({
9611030auth: {
@@ -984,9 +1053,9 @@ describe("trusted-proxy auth", () => {
9841053mode: "trusted-proxy",
9851054allowTailscale: false,
9861055trustedProxy: trustedProxyConfig,
987-token: "secret",
1056+password: "secret", // pragma: allowlist secret
9881057},
989-connectAuth: null,
1058+connectAuth: { password: "secret" },
9901059trustedProxies: ["127.0.0.1"],
9911060req: {
9921061socket: { remoteAddress: "127.0.0.1" },
@@ -1048,8 +1117,8 @@ describe("trusted-proxy auth", () => {
1048111710491118it("still fails closed when trusted-proxy config is missing", async () => {
10501119const res = await authorizeLocalDirect({
1051-token: "secret",
1052-connectToken: "secret",
1120+password: "secret", // pragma: allowlist secret
1121+connectPassword: "secret", // pragma: allowlist secret
10531122trustedProxy: undefined,
10541123});
10551124expect(res.ok).toBe(false);
@@ -1058,8 +1127,8 @@ describe("trusted-proxy auth", () => {
1058112710591128it("still fails closed when trusted proxies are not configured", async () => {
10601129const res = await authorizeLocalDirect({
1061-token: "secret",
1062-connectToken: "secret",
1130+password: "secret", // pragma: allowlist secret
1131+connectPassword: "secret", // pragma: allowlist secret
10631132trustedProxies: [],
10641133});
10651134expect(res.ok).toBe(false);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。