|
1 | | -import { describe, it, expect, beforeEach } from "vitest"; |
| 1 | +import { describe, it, expect, beforeEach, afterEach } from "vitest"; |
2 | 2 | import { runCommand } from "@oclif/test"; |
| 3 | +import * as fs from "node:fs"; |
| 4 | +import * as os from "node:os"; |
| 5 | +import * as path from "node:path"; |
3 | 6 | import { getMockAblyRest } from "../../../../helpers/mock-ably-rest.js"; |
4 | 7 | import { |
5 | 8 | standardHelpTests, |
@@ -307,4 +310,91 @@ describe("push:devices:save command", () => { |
307 | 310 | expect(error?.message).toContain("must be a JSON object"); |
308 | 311 | }); |
309 | 312 | }); |
| 313 | + |
| 314 | + // In web CLI mode --data must never be read from the server's filesystem. |
| 315 | + // The @file shortcut is local-CLI only. |
| 316 | + describe("web CLI file-read restriction", () => { |
| 317 | + let originalWebCliMode: string | undefined; |
| 318 | + let secretFile: string; |
| 319 | + |
| 320 | + beforeEach(() => { |
| 321 | + originalWebCliMode = process.env.ABLY_WEB_CLI_MODE; |
| 322 | + secretFile = path.join(os.tmpdir(), `vul506-device-${process.pid}.json`); |
| 323 | + fs.writeFileSync( |
| 324 | + secretFile, |
| 325 | + '{"id":"device-2","platform":"android","formFactor":"tablet","push":{"recipient":{"transportType":"fcm","registrationToken":"tok"}}}', |
| 326 | + ); |
| 327 | + }); |
| 328 | + |
| 329 | + afterEach(() => { |
| 330 | + if (originalWebCliMode === undefined) { |
| 331 | + delete process.env.ABLY_WEB_CLI_MODE; |
| 332 | + } else { |
| 333 | + process.env.ABLY_WEB_CLI_MODE = originalWebCliMode; |
| 334 | + } |
| 335 | + if (fs.existsSync(secretFile)) fs.rmSync(secretFile); |
| 336 | + }); |
| 337 | + |
| 338 | + it("reads a local --data @file when NOT in web CLI mode", async () => { |
| 339 | + const mock = getMockAblyRest(); |
| 340 | + mock.push.admin.deviceRegistrations.save.mockResolvedValue({ |
| 341 | + id: "device-2", |
| 342 | + }); |
| 343 | + |
| 344 | + const { stderr } = await runCommand( |
| 345 | + ["push:devices:save", "--data", `@${secretFile}`], |
| 346 | + import.meta.url, |
| 347 | + ); |
| 348 | + |
| 349 | + expect(stderr).toContain("Device registration saved"); |
| 350 | + expect(mock.push.admin.deviceRegistrations.save).toHaveBeenCalledWith( |
| 351 | + expect.objectContaining({ id: "device-2", platform: "android" }), |
| 352 | + ); |
| 353 | + }); |
| 354 | + |
| 355 | + it("reads a local --data path input when NOT in web CLI mode", async () => { |
| 356 | + const mock = getMockAblyRest(); |
| 357 | + mock.push.admin.deviceRegistrations.save.mockResolvedValue({ |
| 358 | + id: "device-2", |
| 359 | + }); |
| 360 | + |
| 361 | + const { stderr } = await runCommand( |
| 362 | + ["push:devices:save", "--data", secretFile], |
| 363 | + import.meta.url, |
| 364 | + ); |
| 365 | + |
| 366 | + expect(stderr).toContain("Device registration saved"); |
| 367 | + expect(mock.push.admin.deviceRegistrations.save).toHaveBeenCalledWith( |
| 368 | + expect.objectContaining({ id: "device-2", platform: "android" }), |
| 369 | + ); |
| 370 | + }); |
| 371 | + |
| 372 | + it("rejects --data @file references in web CLI mode", async () => { |
| 373 | + process.env.ABLY_WEB_CLI_MODE = "true"; |
| 374 | + const mock = getMockAblyRest(); |
| 375 | + |
| 376 | + const { error } = await runCommand( |
| 377 | + ["push:devices:save", "--data", `@${secretFile}`], |
| 378 | + import.meta.url, |
| 379 | + ); |
| 380 | + |
| 381 | + expect(error).toBeDefined(); |
| 382 | + expect(error?.message).toContain("not supported in the web CLI"); |
| 383 | + expect(mock.push.admin.deviceRegistrations.save).not.toHaveBeenCalled(); |
| 384 | + }); |
| 385 | + |
| 386 | + it("rejects a --data path input in web CLI mode without reading it", async () => { |
| 387 | + process.env.ABLY_WEB_CLI_MODE = "true"; |
| 388 | + const mock = getMockAblyRest(); |
| 389 | + |
| 390 | + const { error } = await runCommand( |
| 391 | + ["push:devices:save", "--data", secretFile], |
| 392 | + import.meta.url, |
| 393 | + ); |
| 394 | + |
| 395 | + expect(error).toBeDefined(); |
| 396 | + expect(error?.message).toContain("not supported in the web CLI"); |
| 397 | + expect(mock.push.admin.deviceRegistrations.save).not.toHaveBeenCalled(); |
| 398 | + }); |
| 399 | + }); |
310 | 400 | }); |
0 commit comments