This issue has been assessed as high severity. Review affected configurations immediately.
AWS CodeArtifact solves a real problem: it gives teams a private repository for internal packages with built-in caching of public registry dependencies, fine-grained IAM access control, and integration with standard package managers (npm, pip, Maven, NuGet). It also introduces a supply chain risk that many teams do not fully reason about until something goes wrong: upstream proxy behaviour.
When CodeArtifact is configured with an upstream connection to a public registry — npm public, PyPI, Maven Central — it will fetch packages from that public source if they are not found in the private domain. This is the mechanism that dependency confusion attacks exploit.
How Dependency Confusion Attacks Work Against CodeArtifact
Dependency confusion (sometimes called namespace confusion) works by publishing a malicious package to a public registry with the same name as an internal private package, but a higher version number.
The attack chain:
- An attacker identifies an internal package name. This can be through leaked build scripts, error messages, job postings that list internal tooling, or OSINT from public repositories.
- The attacker publishes a package with that name to npm, PyPI, or Maven Central at version 99.0.0 (or any number higher than the internal version).
- A developer runs
pip install internal-utilsornpm install @company/internal-lib. The package manager queries CodeArtifact; CodeArtifact queries the upstream public registry. If the public version number is higher, the package manager may prefer the public version — and installs the attacker’s package.
The variant that targets CodeArtifact specifically: if CodeArtifact’s upstream source connection is enabled and the internal package exists only in the private repository at version 1.2.0, but the attacker publishes version 2.0.0 publicly, the resolution behaviour depends on which registry the client prioritises. In many default configurations, the higher version wins.
This attack class was demonstrated publicly in 2021 (Alex Birsan’s research), exploited against Apple, Microsoft, PayPal, and others, and remains active in 2026. Self-replicating npm worms like Shai-Hulud (documented in mid-2026) have automated discovery and exploitation of this pattern at scale.
Auditing Your Current CodeArtifact Configuration
Before implementing mitigations, map your existing exposure:
List all domains and repositories:
aws codeartifact list-domains --query 'domains[*].name'
aws codeartifact list-repositories --domain YOUR_DOMAIN \
--query 'repositories[*].{name:name,upstreams:upstreams}'
Check upstream source connections:
aws codeartifact describe-repository \
--domain YOUR_DOMAIN \
--repository YOUR_REPO \
--query 'repository.upstreams'
Any repository with an upstream pointing to public:npmjs, public:pypi, public:maven-central, or similar is exposed to dependency confusion if it also hosts internal packages.
Identify packages with matching public names:
# List all packages in your private repository
aws codeartifact list-packages \
--domain YOUR_DOMAIN \
--repository YOUR_INTERNAL_REPO \
--output table
Cross-reference this list against public registry search to identify internal package names that an attacker could squat.
Mitigation 1: Separate Internal and Proxy Repositories
The most robust architectural fix is separating internal packages from public package proxying. Use two repositories:
- internal-packages: holds only your private packages, no upstream connection
- public-proxy: proxy-only repository with upstream connections to public registries, no internal packages
Client package manager configuration points to a third repository that aggregates both, with internal-packages checked first.
# Create internal-only repository (no upstream)
aws codeartifact create-repository \
--domain YOUR_DOMAIN \
--repository internal-packages \
--description "Internal packages only - no public upstream"
# Create public proxy repository
aws codeartifact create-repository \
--domain YOUR_DOMAIN \
--repository public-proxy \
--upstreams upstreamRepositoryName=npm-store
# Create aggregated repository (clients connect here)
aws codeartifact create-repository \
--domain YOUR_DOMAIN \
--repository company-packages \
--upstreams upstreamRepositoryName=internal-packages \
--upstreams upstreamRepositoryName=public-proxy
Package resolution in the aggregated repository checks internal-packages first. If a package is found there, the public proxy is not consulted — eliminating the confusion vector for internal package names.
Mitigation 2: Namespace Squatting (Defensive Publishing)
Register your internal package names on public registries to prevent attackers from doing so. You do not need to publish actual content — publish a placeholder package that announces the name is reserved and should not be used.
For npm:
# Register a placeholder on npm public
npm init --scope @yourcompany
# Package.json name: @yourcompany/internal-utils
# Description: "Reserved package name. This package is not intended for public use."
npm publish --access public
For PyPI, create a placeholder package that fails installation with a clear error:
# setup.py for defensive squatting
from setuptools import setup
setup(
name="internal-utils",
version="0.0.1",
description="This package name is reserved. You should not be installing this.",
python_requires=">=3.6",
install_requires=[],
)
Publish these to each public registry where your package manager queries upstream.
Mitigation 3: Package Origin Enforcement
AWS CodeArtifact supports origin control policies that specify where a package is allowed to originate from. For internal packages, restrict origin to INTERNAL:
aws codeartifact put-package-origin-configuration \
--domain YOUR_DOMAIN \
--repository internal-packages \
--format npm \
--package your-internal-package \
--restrictions publish=ALLOW,upstream=BLOCK
With upstream=BLOCK, CodeArtifact will not fetch this package name from any upstream source, even if a higher version exists publicly. This is the most direct mitigation for named internal packages you can enumerate.
Mitigation 4: CI/CD Pipeline Integrity Controls
Build pipelines are the primary deployment surface for dependency confusion attacks. Add package integrity verification:
Enforce lockfiles in CI:
# .github/workflows/build.yml
- name: Install dependencies (locked)
run: npm ci # Uses package-lock.json exactly; fails if lock is missing or inconsistent
# For pip:
- name: Install dependencies
run: pip install --require-hashes -r requirements.txt
Pin exact versions with integrity hashes (package-lock.json for npm, hashes in pip requirements):
# Generate integrity-verified requirements
pip install --dry-run your-package && pip-compile --generate-hashes requirements.in
Verify package provenance in CI:
# For npm packages, check registry source
npm view your-package --json | jq '._resolved'
# Should show your CodeArtifact endpoint, not registry.npmjs.org
Mitigation 5: IAM Policy Restrictions
Restrict which roles and identities can publish packages to your private repositories. In most organizations, only CI/CD service accounts and designated package maintainers should be able to publish:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"codeartifact:PublishPackageVersion",
"codeartifact:PutPackageMetadata"
],
"Resource": "arn:aws:codeartifact:REGION:ACCOUNT:package/DOMAIN/REPO/*/*/*",
"Condition": {
"ArnLike": {
"aws:PrincipalArn": [
"arn:aws:iam::ACCOUNT:role/cicd-publisher-role"
]
}
}
}
]
}
Deny publish permissions from developer identity pool roles. This limits the blast radius of a compromised developer credential to package consumption, not package publication.
Detection: Unexpected Package Version Spikes
Set up CloudWatch or CodeArtifact event notifications to alert on packages where the version fetched from upstream is significantly higher than the version previously cached in your repository:
# Enable CodeArtifact event notifications to EventBridge
aws codeartifact put-domain-permissions-policy \
--domain YOUR_DOMAIN \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "codeartifact.amazonaws.com"},
"Action": "codeartifact:PublishPackageVersion",
"Resource": "*"
}]
}'
Monitor for PackageVersionPublished events where the package already existed at a lower version and the new version arrives from an upstream source. This is the canonical dependency confusion trigger.
Summary
CodeArtifact’s upstream proxy functionality is useful but creates a resolution ambiguity that dependency confusion attacks exploit directly. The highest-leverage fixes are: separating internal and proxy repositories, implementing per-package origin controls for known internal packages, defensive squatting on public registries, and enforcing lockfiles in CI/CD pipelines. None of these require significant effort; all materially reduce the attack surface for a class of attack that has compromised major technology companies and remains actively weaponised.