Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/redact-device-authorization-log.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/cli-kit': patch
---

Redact sensitive device authorization values from debug output.
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,48 @@ describe('requestDeviceAuthorization', () => {
expect(got).toEqual(dataExpected)
})

test('does not include authorization credentials in debug output', async () => {
// Given
const outputDebug = vi.spyOn(output, 'outputDebug')
const outputInfo = vi.spyOn(output, 'outputInfo')
const response = new Response(JSON.stringify(data), {status: 200})
vi.mocked(shopifyFetch).mockResolvedValue(response)
vi.mocked(identityFqdn).mockResolvedValue('fqdn.com')
vi.mocked(clientId).mockReturnValue('clientId')

// When
await requestDeviceAuthorization(['scope1', 'scope2'])

// Then
const debugOutput = JSON.stringify(outputDebug.mock.calls)
const infoOutput = JSON.stringify(outputInfo.mock.calls)
expect(debugOutput).toContain('HTTP 200')
expect(debugOutput).toContain('interval=5')
expect(debugOutput).toContain('expires_in=3600')
expect(debugOutput).not.toContain(data.device_code)
expect(debugOutput).not.toContain(data.verification_uri_complete)
expect(infoOutput).toContain(data.user_code)
expect(infoOutput).toContain(data.verification_uri_complete)
})

test('uses an explicit marker when the response omits the polling interval', async () => {
// Given
const outputDebug = vi.spyOn(output, 'outputDebug')
outputDebug.mockClear()
const response = new Response(JSON.stringify({...data, interval: undefined}), {status: 200})
vi.mocked(shopifyFetch).mockResolvedValue(response)
vi.mocked(identityFqdn).mockResolvedValue('fqdn.com')
vi.mocked(clientId).mockReturnValue('clientId')

// When
await requestDeviceAuthorization(['scope1', 'scope2'])

// Then
const debugOutput = JSON.stringify(outputDebug.mock.calls)
expect(debugOutput).toContain('interval=not provided')
expect(debugOutput).not.toContain('interval=undefined')
})

test('opens the browser directly in an interactive terminal', async () => {
// Given
const outputInfo = vi.spyOn(output, 'outputInfo')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ export async function requestDeviceAuthorization(scopes: string[]): Promise<Devi
throw new BugError(errorMessage)
}

outputDebug(outputContent`Received device authorization code: ${outputToken.json(jsonResult)}`)
outputDebug(
outputContent`Received device authorization response (HTTP ${String(response.status)}): interval=${String(jsonResult.interval ?? 'not provided')}, expires_in=${String(jsonResult.expires_in ?? 'not provided')}`,
)
if (!jsonResult.device_code || !jsonResult.verification_uri_complete) {
throw new BugError('Failed to start authorization process')
}
Expand Down
Loading