Root Cause and Patch Analysis: AWS SAM CLI Vulnerabilities

A detailed analysis of two security vulnerabilities found in the AWS Serverless Application Model CLI (AWS SAM CLI) – CVE-2025-3047 and CVE-2025-3048 – along with the code-level issues and fixes.

This blogpost provides a detailed analysis of two security vulnerabilities found in the AWS Serverless Application Model CLI (AWS SAM CLI) – CVE-2025-3047 and CVE-2025-3048 – along with the code-level issues and fixes. Both vulnerabilities involve improper handling of symbolic links (symlinks) during the build process using Docker containers. Each section below covers one CVE with a summary, affected code (with references to the SAM CLI source), the patch and how it resolves the issue, and guidance on remediation.

These flaws involve improper handling of symbolic links (symlinks) during the sam build --use-container process. Both issues affect local development environments (they do not impact deployed AWS services or resources)​, but could allow unauthorized access to files on the host machine by abusing how AWS SAM CLI processes symlinks. Upgrading AWS SAM CLI to the patched versions (1.133.0+ for CVE-2025-3047, and 1.134.0+ for CVE-2025-3048) is strongly recommended​.

GHSA-px37-jpqx-97q9 is a path traversal vulnerability in AWS SAM CLI <= v1.132.0 that allowed unauthorized file access on the host machine during sam build --use-container. When building a serverless application inside a Docker container, SAM CLI would follow symlinks in the project by default. An attacker who could plant a malicious symlink in the project (pointing to a sensitive host file) could leverage the elevated permissions of the Docker container to have that file mounted into the container and copied to a location accessible inside the container. In effect, this meant privileged host files (outside the project directory) could be read and exfiltrated via the build container. The issue was fixed in v1.133.0. (To preserve backward compatibility for legitimate cases, SAM CLI v1.133.0 introduced an opt-in flag --mount-symlinks to re-enable the old behavior if needed​.)

Root Cause & Affected Component

The core issue lies in how AWS SAM CLI mounts project directories and their symlinks into the Docker container used for builds. In the AWS SAM CLI code (module samcli.local.docker.container), prior to the fix, all top-level symlinks in the project directory were automatically resolved and bind-mounted into the container with the same elevated privileges as the container process. The container runs as root by default, so resolving and mounting a symlink pointing to a sensitive host path would give the container access to that file, which an unprivileged host user would normally not have. The vulnerable code did not sufficiently restrict which symlinks to follow/mount.

Specifically, in the function that creates Docker volume mounts for the build, the SAM CLI would unconditionally treat symlinks as actual files/directories to mount. The vulnerability resided in the container orchestration logic of SAM CLI, specifically in the Container.create method of samcli/local/docker/container.py. In vulnerable versions, this method always attempted to resolve and mount symlink targets from the project into the Docker container, regardless of context. The problematic code is shown below, from SAM CLI v1.132.0:

# samcli/local/docker/container.py (v1.132.0 - vulnerable snippet)
if self._host_dir:
    mount_mode = "rw,delegated" if self._mount_with_write else "ro,delegated"
    LOG.info("Mounting %s as %s:%s, inside runtime container", self._host_dir, self._working_dir, mount_mode)
_volumes = {
    self._host_dir: {
        "bind": self._working_dir,
        "mode": mount_mode,
    },
    **self._create_mapped_symlink_files(),  # Always resolve and mount symlinks (vulnerable) 
}

In the above code, _create_mapped_symlink_files() scans for symlinks under the project directory and prepares them to be mounted. Because this was unconditionally included, all symlinks (including those pointing outside the project) would be mounted into the container​. (see aws/aws-sam-cli#7865)

The flaw here is that during a sam build --use-container, SAM CLI treats symlinks as files to mount into the container. If a symlink pointed to, say, /etc/shadow on the host, the Docker container (which might run with elevated privileges) would bind-mount that file. This is a classic symlink-based path traversal, resulting in privilege escalation – host files that the user would normally have no access to could be read by the container and then end up in build output.

Patch (Fixed Code in v1.133.0)

The fix introduces a notion of build context and disables symlink resolution during container builds. In the patched version, Container.create takes an extra parameter indicating context (BUILD vs INVOKE), and it will only resolve symlinks if the context is invocation (when running functions locally), not during builds. Below is the corrected code from the patched version:

# samcli/local/docker/container.py (v1.133.0+ - patched snippet)
if self._host_dir:
    mount_mode = "rw,delegated" if self._mount_with_write else "ro,delegated"
    LOG.info("Mounting %s as %s:%s, inside runtime container", self._host_dir, self._working_dir, mount_mode)
    mapped_symlinks = self._create_mapped_symlink_files() if self._resolve_symlinks(context) else {} 
_volumes = {
    self._host_dir: {
        "bind": self._working_dir,
        "mode": mount_mode,
    },
    **mapped_symlinks,  # Only mount symlinks if explicitly allowed by context (not in build) 
}

In the patched code, _create_mapped_symlink_files() is wrapped behind a context check. The new ContainerContext enum defines contexts like BUILD and INVOKE, and _resolve_symlinks(context) returns False for build context​. Thus, mapped_symlinks will be an empty dict during builds, meaning no symlinks are mounted into the container by default.

Code Change Reference

The fix was implemented in Pull Request#7865 (“fix: Resolve symlinks on local invoke only”) and released as part of v1.133.0. The GitHub diff shows the introduction of the ContainerContext and the conditional mounting logic. By not mounting symlink targets during build, the container no longer gains access to files outside the project directory. (If a user does want to allow symlinks to host paths, they must now opt in via the --mount-symlinks flag​, which was added following this fix.)

How the Patch Resolves the Issue

After the patch, any symlinks in the project will no longer be followed during the build phase. They’ll simply remain as symlinks in the container (pointing to paths that won’t be mounted) or be ignored, rather than being replaced by the contents of their target. This closes the hole where an attacker could trick the build process into copying sensitive host files. In short, the build container’s view is now confined to the project directory itself (plus explicitly allowed volumes), eliminating the unintended privilege escalation.

Remediation

All users should upgrade to AWS SAM CLI v1.133.0 or later to get this fix. After upgrading, the default behavior is safe. Only if you explicitly trust your project and need the old behavior should you use sam build --use-container --mount-symlinks. For most developers, leaving this flag off (the default) is recommended to ensure that symlinks cannot traverse outside the workspace. It’s also good practice to review any symlinks in your projects to ensure they do not point to sensitive locations.

GHSA-pp64-wj43-xqcr​ is a related vulnerability affecting AWS SAM CLI <= v1.133.0 (fixed in v1.134.0). A vulnerability in AWS SAM CLI’s build artifact caching could allow sensitive files to leak from the container back to the host workspace after a build. If a project included symlinks, after running sam build --use-container, the contents of the symlink targets would be copied into the local build cache directory as regular files or folders​. In effect, a developer without access to certain host files could gain access because those files’ contents end up in the .aws-sam build output on the host. For example, a symlink in the project pointing to /secret/config could result in the actual content of /secret/config appearing in the project’s .aws-sam/build folder after the container build, even if the user couldn’t read /secret/config directly.

Root Cause & Affected Component

The core of this issue was in how SAM CLI copied files out of the container (or from the build process) into the local project’s build artifacts directory. The function responsible for file copying is found in samcli/lib/utils/osutils.py, specifically the custom copytree utility. In vulnerable versions, this function used Python’s shutil.copy2 without specifying follow_symlinks=False, which by default follows symlinks and copies the file contents. The snippet below (from v1.133.0) shows the problematic logic:

# samcli/lib/utils/osutils.py (v1.133.0 - vulnerable snippet)
# ... inside osutils.copytree ...
else:
    try:
        shutil.copy2(new_source, new_destination)  # follow_symlinks is True by default (vulnerable)
    except OSError as e:
        if e.errno != errno.EINVAL:
            raise e

Here, if new_source is a symlink, shutil.copy2 will resolve the symlink and copy the target file to new_destination. There was no flag to tell it to preserve the symlink. Thus, a symlink to a sensitive file would result in that file’s contents appearing in the destination directory. (see aws/aws-sam-cli#7890)

In practical terms, imagine the build process created a symlink config -> /etc/secret-config (perhaps as part of layering dependencies or as left over from CVE-2025-3047’s scenario). The above code would copy the contents of /etc/secret-config into the local build output as config. A local user who couldn’t read /etc/secret-config directly could now simply open the file under .aws-sam/build/.../config and see its contents.

Patch (Fixed Code in v1.134.0)

The fix was straightforward – preserve symlinks instead of following them when copying. In Python’s shutil, this is done by passing follow_symlinks=False. The patched code (v1.134.0) changes the copy call as follows:

# samcli/lib/utils/osutils.py (v1.134.0 - patched snippet)
else:
    try:
        shutil.copy2(new_source, new_destination, follow_symlinks=False)  # Do not follow symlinks (fixed)
    except OSError as e:
        if e.errno != errno.EINVAL:
            raise e

With follow_symlinks=False, if new_source is a symlink, the function will copy the symlink itself rather than the file it points to​. In other words, the build output will contain a symlink with the same target, instead of a real file with the target’s contents.

Code Change Reference

The change was made in Pull Request#7890 (“fix: Keep symlinks when copying files after build”) and released in v1.134.0. The GitHub diff of this PR confirms the addition of the follow_symlinks=False parameter to shutil.copy2, along with updated unit tests to ensure symlinks are preserved. The PR description explicitly notes: “Symlinks will stop being transformed into copies of the files and keep their symlink status.”​ This means the build artifacts will contain symlinks (which reference the original file paths) instead of unauthorized copies of the file data.

How the Patch Resolves the Issue

After this fix, the SAM CLI’s post-build file copying no longer leaks file contents. If a symlink was created during build, it will remain a symlink in the output. The local user won’t magically gain read access to the target’s content – they’ll just see a symlink that still points to the original path. Unless the user already had permission to read the target file, the symlink in the output is harmless (it will error out if dereferenced without proper access). Essentially, the confidentiality impact is mitigated: sensitive files aren’t inadvertently materialized into the user’s workspace​. This fix complements the CVE-2025-3047 patch: it stopped the container from grabbing host files via symlinks; the fix for CVE-2025-3048 stops any that did get through (or existed legitimately) from being persisted as plain files in the output.

Remediation

Users should upgrade to AWS SAM CLI v1.134.0 or newer to obtain this patch. Once on v1.134.0+, the build process will preserve symlinks by default, fixing this vulnerability. After upgrading, it’s recommended to clean and rebuild any SAM applications to ensure that any cached build artifacts are regenerated under the new safer behavior​ (the AWS security bulletin advises running a fresh sam build --use-container after upgrading). There are no workarounds for this issue in older versions besides manually removing sensitive symlinks or not using container builds, so upgrading is the only robust solution. In general, treat the local SAM CLI build folder as sensitive output – with the patch it should no longer contain unexpected secrets, but it’s good practice to keep an eye on what ends up in your build artifacts.

Security Impact and Conclusion

Both CVE-2025-3047 and CVE-2025-3048 involve symlink handling weaknesses that could lead to exposure of confidential files during local builds. CVE-2025-3047 could allow files to be read within the Docker container (and potentially copied out), while CVE-2025-3048 could allow those files to end up in the local output where a user or attacker could read them later. These vulnerabilities were rated moderate severity, as they require some user interaction (running a build on a malicious project) but could result in high confidentiality impact. The coordinated patches ensure that by default, SAM CLI will neither follow symlinks during containerized builds nor copy symlink targets into output.

Developers and DevSecOps professionals using SAM CLI should make sure their CLI is up-to-date (v1.134.0 or later) and remain cautious about projects containing unexpected symlinks. If you maintain forked or customized versions of SAM CLI, you must incorporate these same fixes​. By understanding these code changes, one can appreciate how a small logic tweak – such as adding a conditional or a function parameter – can close off a serious security hole. Always consider the security of file-handling, especially when it comes to symlinks and container interactions, to prevent similar path traversal issues in your own projects.

References