



























@@ -144,6 +144,7 @@ describe("gateway auth", () => {
144144{ name: "X-Forwarded-For", headers: { "x-forwarded-for": "203.0.113.10" } },
145145{ name: "X-Forwarded-Proto", headers: { "x-forwarded-proto": "https" } },
146146{ name: "X-Forwarded-Host", headers: { "x-forwarded-host": "gateway.example" } },
147+{ name: "X-Forwarded-User", headers: { "x-forwarded-user": "nick@example.com" } },
147148{ name: "X-Real-IP", headers: { "x-real-ip": "203.0.113.10" } },
148149])("treats $name as forwarded request evidence", ({ headers }) => {
149150const req = {
@@ -1024,35 +1025,35 @@ describe("trusted-proxy auth", () => {
10241025expect(res.reason).toBe("trusted_proxy_loopback_source");
10251026});
102610271027-it("rejects local-direct password credentials when trusted-proxy auth fails", async () => {
1028+it("accepts local-direct password fallback when trusted-proxy auth fails", async () => {
10281029const limiter = createLimiterSpy();
10291030const res = await authorizeLocalDirect({
10301031password: "local-password", // pragma: allowlist secret
10311032connectPassword: "local-password", // pragma: allowlist secret
10321033rateLimiter: limiter,
10331034});
103410351035-expect(res).toEqual({ ok: false, reason: "trusted_proxy_loopback_source" });
1036-expect(limiter.check).not.toHaveBeenCalled();
1037-expect(limiter.reset).not.toHaveBeenCalled();
1036+expect(res).toEqual({ ok: true, method: "password" });
1037+expect(limiter.check).toHaveBeenCalledWith("127.0.0.1", "shared-secret");
1038+expect(limiter.reset).toHaveBeenCalledWith("127.0.0.1", "shared-secret");
10381039expect(limiter.recordFailure).not.toHaveBeenCalled();
10391040});
104010411041-it("ignores wrong local-direct password credentials when trusted-proxy auth fails", async () => {
1042+it("rejects wrong local-direct password fallback and records the failure", async () => {
10421043const limiter = createLimiterSpy();
10431044const res = await authorizeLocalDirect({
10441045password: "local-password", // pragma: allowlist secret
10451046connectPassword: "wrong-password", // pragma: allowlist secret
10461047rateLimiter: limiter,
10471048});
104810491049-expect(res).toEqual({ ok: false, reason: "trusted_proxy_loopback_source" });
1050-expect(limiter.check).not.toHaveBeenCalled();
1051-expect(limiter.recordFailure).not.toHaveBeenCalled();
1050+expect(res).toEqual({ ok: false, reason: "password_mismatch" });
1051+expect(limiter.check).toHaveBeenCalledWith("127.0.0.1", "shared-secret");
1052+expect(limiter.recordFailure).toHaveBeenCalledWith("127.0.0.1", "shared-secret");
10521053expect(limiter.reset).not.toHaveBeenCalled();
10531054});
105410551055-it("does not apply shared-secret rate limits to trusted-proxy failures", async () => {
1056+it("enforces rate-limit lockout before local-direct password fallback", async () => {
10561057const limiter = createLimiterSpy();
10571058limiter.check.mockReturnValueOnce({
10581059allowed: false,
@@ -1066,12 +1067,29 @@ describe("trusted-proxy auth", () => {
10661067rateLimiter: limiter,
10671068});
106810691069-expect(res).toEqual({ ok: false, reason: "trusted_proxy_loopback_source" });
1070-expect(limiter.check).not.toHaveBeenCalled();
1070+expect(res).toEqual({
1071+ok: false,
1072+reason: "rate_limited",
1073+rateLimited: true,
1074+retryAfterMs: 2500,
1075+});
10711076expect(limiter.recordFailure).not.toHaveBeenCalled();
10721077expect(limiter.reset).not.toHaveBeenCalled();
10731078});
107410791080+it("accepts local-direct password fallback before required-header failure", async () => {
1081+const res = await authorizeLocalDirect({
1082+password: "local-password", // pragma: allowlist secret
1083+connectPassword: "local-password", // pragma: allowlist secret
1084+trustedProxy: {
1085+ ...trustedProxyConfig,
1086+allowLoopback: true,
1087+},
1088+});
1089+1090+expect(res).toEqual({ ok: true, method: "password" });
1091+});
1092+10751093it("keeps local-direct trusted-proxy on proxy failure when no password is supplied", async () => {
10761094const res = await authorizeLocalDirect({
10771095password: "local-password", // pragma: allowlist secret
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。