import type { ECRClientConfig } from '@aws-sdk/client-ecr'; import { ECR } from '@aws-sdk/client-ecr'; import { logger } from '../../../logger/index.ts'; import type { HostRule } from '../../../types/index.ts'; import type { HttpError } from '../../../util/http/index.ts'; import type { HttpResponse } from '../../../util/http/types.ts'; import { regEx } from '../../../util/regex.ts'; import { addSecretForSanitizing } from '../../../util/sanitize.ts'; export const ecrRegex = regEx( /\d+\.(?:dkr\.ecr|dkr-ecr)(?:-fips)?\.(?[-a-z0-9]+)\.(?:amazonaws\.com|on\.aws|amazonaws\.com\.cn|on\.amazonwebservices\.com\.cn|amazonaws\.eu|on\.amazonwebservices\.eu|c2s\.ic\.gov|on\.aws\.ic\.gov|sc2s\.sgov\.gov|on\.aws\.scloud|scloud\.adc-e\.uk|on\.cloud-aws\.adc-e\.uk|csp\.hci\.ic\.gov|on\.aws\.hci\.ic\.gov|)/, ); export const ecrPublicRegex = regEx(/public\.ecr\.aws|ecr-public\.aws\.com/); export async function getECRAuthToken( region: string, opts: HostRule, ): Promise { const config: ECRClientConfig = { region }; if (opts.username === `AWS` && opts.password) { logger.trace( `AWS user specified, encoding basic auth credentials for ECR registry`, ); return Buffer.from(`AWS:${opts.password}`).toString('base64'); } if (opts.username && opts.password) { logger.trace( `Using AWS accessKey to get Authorization token for ECR registry`, ); config.credentials = { accessKeyId: opts.username, secretAccessKey: opts.password, ...(opts.token && { sessionToken: opts.token }), }; } const ecr = new ECR(config); try { const data = await ecr.getAuthorizationToken({}); const authorizationToken = data?.authorizationData?.[0]?.authorizationToken; if (authorizationToken) { // sanitize token addSecretForSanitizing(authorizationToken); return authorizationToken; } logger.warn( 'Could not extract authorizationToken from ECR getAuthorizationToken response', ); } catch (err) { logger.trace({ err }, 'err'); logger.warn('ECR getAuthorizationToken error'); } return null; } // https://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_DescribeRepositories.html#ECR-DescribeRepositories-request-maxResults export function isECRMaxResultsResponse(resp: HttpResponse): boolean { const body = resp.body as { errors?: { message?: string }[] } | undefined; return !!( resp.statusCode === 405 && resp.headers?.['docker-distribution-api-version'] && body?.errors?.[0]?.message?.includes( 'Member must have value less than or equal to 1000', ) ); } export function isECRMaxResultsError(err: HttpError): boolean { const resp = err.response as HttpResponse | undefined; return !!resp && isECRMaxResultsResponse(resp); }