Skip to main content

CodeArtifact External Public Publishing Disabled

Overview

This check verifies that your internal AWS CodeArtifact packages are protected from accepting package versions from external public sources. When an internal package allows "upstream" ingestion, it can pull in versions from public repositories--which opens the door to a serious attack called dependency confusion.

Risk

If this check fails, your internal packages could be hijacked by malicious actors who publish higher-versioned packages to public repositories. When your build system requests a package, it may unknowingly download the attacker's version instead of your legitimate internal one.

Potential consequences:

  • Malicious code running in your builds and applications
  • Stolen secrets, credentials, or sensitive data
  • Disrupted CI/CD pipelines and service outages

Severity: Critical

Remediation Steps

Prerequisites

  • Access to the AWS Console with permissions to modify CodeArtifact packages, or
  • AWS CLI installed and configured with appropriate credentials
Required IAM permissions

Your IAM user or role needs the following permissions:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"codeartifact:PutPackageOriginConfiguration",
"codeartifact:DescribePackage",
"codeartifact:ListPackages"
],
"Resource": "*"
}
]
}

For production, scope the Resource to specific domains and repositories.

AWS Console Method

  1. Open the AWS CodeArtifact console
  2. In the left navigation, click Repositories
  3. Select the repository containing the flagged package
  4. Click on the package name that was identified in the Prowler finding
  5. Click the Origin controls tab
  6. Click Edit
  7. Set Upstream to Block
  8. Leave Publish set to Allow (unless you want to block direct publishing too)
  9. Click Save

Repeat for each package flagged by Prowler.

AWS CLI method

Use the put-package-origin-configuration command to block upstream ingestion:

aws codeartifact put-package-origin-configuration \
--domain <your-domain-name> \
--repository <your-repository-name> \
--format <package-format> \
--package <package-name> \
--restrictions publish=ALLOW,upstream=BLOCK \
--region us-east-1

Parameters:

  • --domain: Your CodeArtifact domain name
  • --repository: The repository containing the package
  • --format: Package type (one of: npm, pypi, maven, nuget, generic, ruby, swift, cargo)
  • --package: The name of the package to protect
  • --restrictions: Set upstream=BLOCK to prevent external ingestion

Example for an npm package:

aws codeartifact put-package-origin-configuration \
--domain my-company-domain \
--repository internal-packages \
--format npm \
--package my-internal-lib \
--restrictions publish=ALLOW,upstream=BLOCK \
--region us-east-1

For packages with namespaces (like scoped npm packages or Maven group IDs):

aws codeartifact put-package-origin-configuration \
--domain my-company-domain \
--repository internal-packages \
--format npm \
--namespace "@mycompany" \
--package core-utils \
--restrictions publish=ALLOW,upstream=BLOCK \
--region us-east-1

Bulk remediation script:

To fix all internal packages in a repository at once:

#!/bin/bash
DOMAIN="my-company-domain"
REPO="internal-packages"
FORMAT="npm"
REGION="us-east-1"

# List all packages and update each one
aws codeartifact list-packages \
--domain "$DOMAIN" \
--repository "$REPO" \
--format "$FORMAT" \
--region "$REGION" \
--query 'packages[*].package' \
--output text | while read PACKAGE; do
echo "Updating package: $PACKAGE"
aws codeartifact put-package-origin-configuration \
--domain "$DOMAIN" \
--repository "$REPO" \
--format "$FORMAT" \
--package "$PACKAGE" \
--restrictions publish=ALLOW,upstream=BLOCK \
--region "$REGION"
done
CloudFormation

AWS CloudFormation does not currently support AWS::CodeArtifact::PackageOriginConfiguration as a native resource type. You have two options:

Option 1: Use a Custom Resource with Lambda

AWSTemplateFormatVersion: '2010-09-09'
Description: CodeArtifact package origin configuration via custom resource

Parameters:
DomainName:
Type: String
Description: CodeArtifact domain name
RepositoryName:
Type: String
Description: CodeArtifact repository name
PackageFormat:
Type: String
AllowedValues:
- npm
- pypi
- maven
- nuget
- generic
- ruby
- swift
- cargo
PackageName:
Type: String
Description: Name of the package to configure

Resources:
PackageOriginConfigLambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: CodeArtifactAccess
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- codeartifact:PutPackageOriginConfiguration
Resource: '*'

PackageOriginConfigLambda:
Type: AWS::Lambda::Function
Properties:
Runtime: python3.11
Handler: index.handler
Role: !GetAtt PackageOriginConfigLambdaRole.Arn
Timeout: 30
Code:
ZipFile: |
import boto3
import cfnresponse

def handler(event, context):
try:
if event['RequestType'] in ['Create', 'Update']:
client = boto3.client('codeartifact')
client.put_package_origin_configuration(
domain=event['ResourceProperties']['Domain'],
repository=event['ResourceProperties']['Repository'],
format=event['ResourceProperties']['Format'],
package=event['ResourceProperties']['Package'],
restrictions={
'publish': 'ALLOW',
'upstream': 'BLOCK'
}
)
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
except Exception as e:
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': str(e)})

PackageOriginConfig:
Type: Custom::PackageOriginConfiguration
Properties:
ServiceToken: !GetAtt PackageOriginConfigLambda.Arn
Domain: !Ref DomainName
Repository: !Ref RepositoryName
Format: !Ref PackageFormat
Package: !Ref PackageName

Option 2: Post-deployment script

Deploy your CodeArtifact resources with CloudFormation, then run the AWS CLI command as a post-deployment step in your CI/CD pipeline.

Terraform

Terraform does not have a native resource for CodeArtifact package origin configuration. Use the null_resource with a local-exec provisioner or the AWS CLI.

Using null_resource:

variable "domain_name" {
description = "CodeArtifact domain name"
type = string
}

variable "repository_name" {
description = "CodeArtifact repository name"
type = string
}

variable "package_format" {
description = "Package format (npm, pypi, maven, etc.)"
type = string
}

variable "package_name" {
description = "Name of the package to configure"
type = string
}

resource "null_resource" "package_origin_config" {
triggers = {
domain = var.domain_name
repository = var.repository_name
format = var.package_format
package = var.package_name
}

provisioner "local-exec" {
command = <<-EOT
aws codeartifact put-package-origin-configuration \
--domain ${var.domain_name} \
--repository ${var.repository_name} \
--format ${var.package_format} \
--package ${var.package_name} \
--restrictions publish=ALLOW,upstream=BLOCK \
--region us-east-1
EOT
}
}

Using a module for multiple packages:

variable "packages_to_protect" {
description = "List of packages to configure origin controls"
type = list(object({
domain = string
repository = string
format = string
package = string
namespace = optional(string)
}))
}

resource "null_resource" "package_origin_configs" {
for_each = { for idx, pkg in var.packages_to_protect : "${pkg.domain}-${pkg.repository}-${pkg.package}" => pkg }

triggers = {
domain = each.value.domain
repository = each.value.repository
format = each.value.format
package = each.value.package
namespace = each.value.namespace
}

provisioner "local-exec" {
command = <<-EOT
aws codeartifact put-package-origin-configuration \
--domain ${each.value.domain} \
--repository ${each.value.repository} \
--format ${each.value.format} \
--package ${each.value.package} \
${each.value.namespace != null ? "--namespace ${each.value.namespace}" : ""} \
--restrictions publish=ALLOW,upstream=BLOCK \
--region us-east-1
EOT
}
}

Verification

After making changes, verify the configuration is correct:

  1. In the AWS Console, navigate to the package and confirm Upstream shows Block in the Origin controls tab
  2. Re-run the Prowler check to confirm remediation:
    prowler aws --check codeartifact_packages_external_public_publishing_disabled -r us-east-1
CLI verification

Use the describe-package command to verify the origin configuration:

aws codeartifact describe-package \
--domain <your-domain-name> \
--repository <your-repository-name> \
--format <package-format> \
--package <package-name> \
--region us-east-1 \
--query 'package.originConfiguration.restrictions'

Expected output:

{
"publish": "ALLOW",
"upstream": "BLOCK"
}

Additional Resources

Notes

  • Preemptive protection: You can call put-package-origin-configuration on package names that do not yet exist in your repository. This creates a placeholder that blocks upstream ingestion before the first version is even published.

  • Package groups: For large-scale protection, consider using CodeArtifact package groups to apply origin controls to multiple packages matching a pattern.

  • Private namespaces: Use unique namespaces (like @yourcompany/ for npm) for internal packages to reduce the risk of name collisions with public packages.

  • No service disruption: Changing origin controls does not affect existing package versions already in your repository. It only controls where new versions can come from.