update ts docs

This commit is contained in:
Salman Chishti 2025-03-13 04:47:49 -07:00
parent fc482662af
commit 6876e2a664
5 changed files with 129 additions and 36 deletions

View File

@ -73,13 +73,23 @@ export function getBackendIdsFromToken(): BackendIds {
/** /**
* Masks the `sig` parameter in a URL and sets it as a secret. * Masks the `sig` parameter in a URL and sets it as a secret.
* @param url The URL containing the `sig` parameter. *
* @returns A masked URL where the sig parameter value is replaced with '***' if found, * @param url - The URL containing the signature parameter to mask
* or the original URL if no sig parameter is present. * @remarks
* This function attempts to parse the provided URL and identify the 'sig' query parameter.
* If found, it registers both the raw and URL-encoded signature values as secrets using
* the Actions `setSecret` API, which prevents them from being displayed in logs.
*
* The function handles errors gracefully if URL parsing fails, logging them as debug messages.
*
* @example
* ```typescript
* // Mask a signature in an Azure SAS token URL
* maskSigUrl('https://example.blob.core.windows.net/container/file.txt?sig=abc123&se=2023-01-01');
* ```
*/ */
export function maskSigUrl(url: string): string { export function maskSigUrl(url: string): void {
if (!url) return url if (!url) return
try { try {
const parsedUrl = new URL(url) const parsedUrl = new URL(url)
const signature = parsedUrl.searchParams.get('sig') const signature = parsedUrl.searchParams.get('sig')
@ -88,7 +98,6 @@ export function maskSigUrl(url: string): string {
setSecret(signature) setSecret(signature)
setSecret(encodeURIComponent(signature)) setSecret(encodeURIComponent(signature))
parsedUrl.searchParams.set('sig', '***') parsedUrl.searchParams.set('sig', '***')
return parsedUrl.toString()
} }
} catch (error) { } catch (error) {
debug( debug(
@ -97,11 +106,28 @@ export function maskSigUrl(url: string): string {
}` }`
) )
} }
return url
} }
/** /**
* Masks any URLs containing signature parameters in the provided object * Masks sensitive information in URLs containing signature parameters.
* Currently supports masking 'sig' parameters in the 'signed_upload_url'
* and 'signed_download_url' properties of the provided object.
*
* @param body - The object should contain a signature
* @remarks
* This function extracts URLs from the object properties and calls maskSigUrl
* on each one to redact sensitive signature information. The function doesn't
* modify the original object; it only marks the signatures as secrets for
* logging purposes.
*
* @example
* ```typescript
* const responseBody = {
* signed_upload_url: 'https://example.com?sig=abc123',
* signed_download_url: 'https://example.com?sig=def456'
* };
* maskSecretUrls(responseBody);
* ```
*/ */
export function maskSecretUrls(body: Record<string, unknown> | null): void { export function maskSecretUrls(body: Record<string, unknown> | null): void {
if (typeof body !== 'object' || body === null) { if (typeof body !== 'object' || body === null) {

View File

@ -8,34 +8,28 @@ describe('maskSigUrl', () => {
jest.clearAllMocks() jest.clearAllMocks()
}) })
it('returns the original URL if no sig parameter is present', () => { it('does nothing if no sig parameter is present', () => {
const url = 'https://example.com' const url = 'https://example.com'
const maskedUrl = maskSigUrl(url) maskSigUrl(url)
expect(maskedUrl).toBe(url)
expect(setSecret).not.toHaveBeenCalled() expect(setSecret).not.toHaveBeenCalled()
}) })
it('masks the sig parameter in the middle of the URL and sets it as a secret', () => { it('masks the sig parameter in the middle of the URL and sets it as a secret', () => {
const url = 'https://example.com/?param1=value1&sig=12345&param2=value2' const url = 'https://example.com/?param1=value1&sig=12345&param2=value2'
const maskedUrl = maskSigUrl(url) maskSigUrl(url)
expect(maskedUrl).toBe(
'https://example.com/?param1=value1&sig=***&param2=value2'
)
expect(setSecret).toHaveBeenCalledWith('12345') expect(setSecret).toHaveBeenCalledWith('12345')
expect(setSecret).toHaveBeenCalledWith(encodeURIComponent('12345')) expect(setSecret).toHaveBeenCalledWith(encodeURIComponent('12345'))
}) })
it('returns the original URL if it is empty', () => { it('does nothing if the URL is empty', () => {
const url = '' const url = ''
const maskedUrl = maskSigUrl(url) maskSigUrl(url)
expect(maskedUrl).toBe('')
expect(setSecret).not.toHaveBeenCalled() expect(setSecret).not.toHaveBeenCalled()
}) })
it('handles URLs with fragments', () => { it('handles URLs with fragments', () => {
const url = 'https://example.com?sig=12345#fragment' const url = 'https://example.com?sig=12345#fragment'
const maskedUrl = maskSigUrl(url) maskSigUrl(url)
expect(maskedUrl).toBe('https://example.com/?sig=***#fragment')
expect(setSecret).toHaveBeenCalledWith('12345') expect(setSecret).toHaveBeenCalledWith('12345')
expect(setSecret).toHaveBeenCalledWith(encodeURIComponent('12345')) expect(setSecret).toHaveBeenCalledWith(encodeURIComponent('12345'))
}) })

View File

@ -2,13 +2,23 @@ import {debug, setSecret} from '@actions/core'
/** /**
* Masks the `sig` parameter in a URL and sets it as a secret. * Masks the `sig` parameter in a URL and sets it as a secret.
* @param url The URL containing the `sig` parameter. *
* @returns A masked URL where the sig parameter value is replaced with '***' if found, * @param url - The URL containing the signature parameter to mask
* or the original URL if no sig parameter is present. * @remarks
* This function attempts to parse the provided URL and identify the 'sig' query parameter.
* If found, it registers both the raw and URL-encoded signature values as secrets using
* the Actions `setSecret` API, which prevents them from being displayed in logs.
*
* The function handles errors gracefully if URL parsing fails, logging them as debug messages.
*
* @example
* ```typescript
* // Mask a signature in an Azure SAS token URL
* maskSigUrl('https://example.blob.core.windows.net/container/file.txt?sig=abc123&se=2023-01-01');
* ```
*/ */
export function maskSigUrl(url: string): string { export function maskSigUrl(url: string): void {
if (!url) return url if (!url) return
try { try {
const parsedUrl = new URL(url) const parsedUrl = new URL(url)
const signature = parsedUrl.searchParams.get('sig') const signature = parsedUrl.searchParams.get('sig')
@ -17,7 +27,6 @@ export function maskSigUrl(url: string): string {
setSecret(signature) setSecret(signature)
setSecret(encodeURIComponent(signature)) setSecret(encodeURIComponent(signature))
parsedUrl.searchParams.set('sig', '***') parsedUrl.searchParams.set('sig', '***')
return parsedUrl.toString()
} }
} catch (error) { } catch (error) {
debug( debug(
@ -26,18 +35,34 @@ export function maskSigUrl(url: string): string {
}` }`
) )
} }
return url
} }
/** /**
* Masks any URLs containing signature parameters in the provided object * Masks sensitive information in URLs containing signature parameters.
* Currently supports masking 'sig' parameters in the 'signed_upload_url'
* and 'signed_download_url' properties of the provided object.
*
* @param body - The object should contain a signature
* @remarks
* This function extracts URLs from the object properties and calls maskSigUrl
* on each one to redact sensitive signature information. The function doesn't
* modify the original object; it only marks the signatures as secrets for
* logging purposes.
*
* @example
* ```typescript
* const responseBody = {
* signed_upload_url: 'https://blob.core.windows.net/?sig=abc123',
* signed_download_url: 'https://blob.core/windows.net/?sig=def456'
* };
* maskSecretUrls(responseBody);
* ```
*/ */
export function maskSecretUrls(body: Record<string, unknown> | null): void { export function maskSecretUrls(body: Record<string, unknown> | null): void {
if (typeof body !== 'object' || body === null) { if (typeof body !== 'object' || body === null) {
debug('body is not an object or is null') debug('body is not an object or is null')
return return
} }
if ( if (
'signed_upload_url' in body && 'signed_upload_url' in body &&
typeof body.signed_upload_url === 'string' typeof body.signed_upload_url === 'string'

View File

@ -11,14 +11,37 @@ export interface CommandProperties {
} }
/** /**
* Commands * Issues a command to the GitHub Actions runner
*
* @param command - The command name to issue
* @param properties - Additional properties for the command (key-value pairs)
* @param message - The message to include with the command
* @remarks
* This function outputs a specially formatted string to stdout that the Actions
* runner interprets as a command. These commands can control workflow behavior,
* set outputs, create annotations, mask values, and more.
* *
* Command Format: * Command Format:
* ::name key=value,key=value::message * ::name key=value,key=value::message
* *
* Examples: * @example
* ::warning::This is the message * ```typescript
* ::set-env name=MY_VAR::some value * // Issue a warning annotation
* issueCommand('warning', {}, 'This is a warning message');
* // Output: ::warning::This is a warning message
*
* // Set an environment variable
* issueCommand('set-env', { name: 'MY_VAR' }, 'some value');
* // Output: ::set-env name=MY_VAR::some value
*
* // Add a secret mask
* issueCommand('add-mask', {}, 'secretValue123');
* // Output: ::add-mask::secretValue123
* ```
*
* @internal
* This is an internal utility function that powers the public API functions
* such as setSecret, warning, error, and exportVariable.
*/ */
export function issueCommand( export function issueCommand(
command: string, command: string,

View File

@ -94,7 +94,32 @@ export function exportVariable(name: string, val: any): void {
/** /**
* Registers a secret which will get masked from logs * Registers a secret which will get masked from logs
* @param secret value of the secret *
* @param secret - Value of the secret to be masked
* @remarks
* This function instructs the Actions runner to mask the specified value in any
* logs produced during the workflow run. Once registered, the secret value will
* be replaced with asterisks (***) whenever it appears in console output, logs,
* or error messages.
*
* This is useful for protecting sensitive information such as:
* - API keys
* - Access tokens
* - Authentication credentials
* - URL parameters containing signatures (SAS tokens)
*
* Note that masking only affects future logs; any previous appearances of the
* secret in logs before calling this function will remain unmasked.
*
* @example
* ```typescript
* // Register an API token as a secret
* const apiToken = "abc123xyz456";
* setSecret(apiToken);
*
* // Now any logs containing this value will show *** instead
* console.log(`Using token: ${apiToken}`); // Outputs: "Using token: ***"
* ```
*/ */
export function setSecret(secret: string): void { export function setSecret(secret: string): void {
issueCommand('add-mask', {}, secret) issueCommand('add-mask', {}, secret)