Skip to content

CWE-829: Inclusion of Functionality from Untrusted Control Sphere

Overview

An application loads code, libraries, or other functionality from a source it does not control and has not verified, so whoever controls that source decides what the application runs.

Relationship to Other CWEs

This is the general weakness for including any functionality from an untrusted source. Two narrower CWEs exist for specific variants: CWE-830 covers the same problem specifically for web functionality (scripts, widgets, embedded content), and CWE-98 covers the PHP-specific case of remote file inclusion through include/require. Prefer whichever page matches the specific finding; use this page when the included functionality isn't web content or PHP file inclusion (native libraries, package dependencies, dynamically loaded modules, eval/exec of remote code).

CWE-494 (Download of Code Without Integrity Check) is the same problem seen from the integrity side: the source was one you meant to trust, and nothing verified the bytes that actually arrived. Its guidance on where an expected hash or public key has to come from applies directly to every verification step below.

OWASP Classification

A08:2025 - Software or Data Integrity Failures

Risk

Critical: The included code runs inside the application with the application's privileges, so an attacker who controls it gets whatever access the application has.

Remediation Steps

Core Principle: Load and execute code only from sources whose provenance and integrity you have verified.

Locate the untrusted code inclusion

  • Review the flaw details to identify the file, line number, and code pattern
  • Identify where untrusted code or functionality is being included (dynamic imports, exec(), eval(), third-party libraries)
  • Trace where the code comes from: a URL, user input, an external API, a package manager
  • Determine what privileges the included code has and what data it can reach

Only use trusted, verified components (Primary Defense)

  • Download libraries through official package managers (npm, PyPI, Maven Central, NuGet), not arbitrary URLs
  • Commit a lockfile and install from it. This is the control that actually holds: package-lock.json, Gemfile.lock, poetry.lock, or a pip requirements file in hash-checking mode record a hash for every resolved artefact in your repository, and a mismatch fails the install. Use the command that enforces the lockfile rather than re-resolving it - npm ci rather than npm install, and pip install --require-hashes -r requirements.txt
  • Know what the package manager checks by default, and what it does not. npm verifies the integrity hash recorded for each tarball, but on a first resolve that hash comes from the registry that served the tarball - a same-host checksum, which is a pattern CWE-494 treats as no protection at all. It becomes a real control once the lockfile is committed and reviewed. Registry publisher signatures are checked only when you run npm audit signatures. pip does not verify signatures at all - PyPI removed PGP signature uploads in May 2023 - and checks hashes only when the requirements file supplies them
  • Verify publisher signatures as an explicit step, except where the tooling already does. NuGet is the exception: dotnet restore verifies package signatures with no configuration, always on Windows, and by default on Linux from the .NET 8 SDK onwards (DOTNET_NUGET_SIGNATURE_VERIFICATION=false opts out); on macOS it is off by default. Everywhere else the verification is a step you add - Maven Central PGP signatures, Sigstore attestations, and npm audit signatures all have to be run explicitly, in CI, because no ordinary install command runs them
  • Verify checksums of anything fetched outside a package manager: compare SHA-256 against a hash recorded in your own source, not one fetched alongside the download - a checksum served by the download host only proves the host was consistent with itself
  • Pin exact versions (1.2.3) rather than ranges (^1.0.0, >=1.0.0) for critical dependencies

Pin CDN-Hosted Scripts with Subresource Integrity (Primary Defense for Browser Includes)

When a page loads a script or stylesheet from a host you do not control, Subresource Integrity is the fix rather than a hardening extra: the browser refuses to execute the resource unless the fetched bytes hash to the value in your markup.

<!-- SECURE - browser refuses to run the script if the CDN serves different bytes -->
<script src="https://cdn.example.com/lib-1.2.3.min.js"
        integrity="sha384-BASE64_DIGEST_OF_THE_PINNED_FILE"
        crossorigin="anonymous"></script>

Generate the digest from the exact file you pinned:

openssl dgst -sha384 -binary lib-1.2.3.min.js | openssl base64 -A
  • crossorigin is required, not optional. Browsers will not apply integrity to a no-cors request, so an integrity attribute without crossorigin gives you a resource that never loads. The CDN has to send Access-Control-Allow-Origin for the anonymous request to succeed.
  • As a markup attribute, integrity applies only to <script>, and to <link> with a rel of stylesheet, preload, or modulepreload. Scripted loads are not all outside its reach, though: fetch() takes an integrity option in its RequestInit, and a <script> created with createElement honours its .integrity property before insertion. The gap is a dynamic import(), which takes no integrity argument of its own - see the import map below.
  • Pin the version in the URL too. The digest covers exactly the bytes you hashed, so an unversioned CDN path breaks the page the moment the file is updated. Regenerate the digest with every version bump.

What covers a dynamic import() is the integrity section of an import map - not the map's imports section, which only rewrites specifiers to URLs and enforces nothing. The integrity section pairs a module URL with an SRI digest, and the browser applies it to every import of that URL, static or dynamic:

<!-- SECURE - the digest applies wherever this URL is imported from -->
<script type="importmap">
{
  "imports": { "charting": "https://cdn.example.com/charting-2.1.0.js" },
  "integrity": { "https://cdn.example.com/charting-2.1.0.js": "sha384-BASE64_DIGEST" }
}
</script>

Support for the integrity key arrived in Chrome and Edge 127, Safari 18, and Firefox 138, so current browsers enforce it. A browser that does not understand the key ignores it and loads the module unchecked, with no error - so it is a control for the browsers you support rather than a guarantee across all of them. Self-hosting the module is the option that does not depend on the client.

Apply least privilege and isolation to included functionality

  • Grant a third-party component the minimum permissions it needs
  • Isolate untrusted code from the rest of the application: a sandbox, container, or VM; a separate process with limited system access; or a Web Worker in the browser
  • Use Content Security Policy to restrict which sources a page may load scripts from
  • Never pass untrusted data to exec(), eval() or Function(). Loading a component you chose is a different question from evaluating data at runtime, and that second one is not this weakness: it is CWE-94 (code injection), or CWE-95 (eval injection) where the sink is an eval-style call

Apply additional supply chain protections

  • Remove libraries you no longer use - each one is attack surface, and dependency analysis tools will list them
  • Scan dependencies for known vulnerabilities with npm audit, Snyk, Dependabot, or OWASP Dependency-Check
  • Assess a third-party library before adopting it: security practices, maintenance status, CVE history

Monitor and audit third-party usage

  • Review and update third-party dependencies on a schedule (monthly security patching, quarterly major updates)
  • Log and alert on unexpected functionality: new network connections, file access, unexpected API calls
  • Use Software Composition Analysis (SCA) tools to track dependencies and vulnerabilities
  • Monitor package manager security advisories (GitHub Security Advisories, npm security alerts)

Test the remediation

  • Verify the specific untrusted source is no longer used
  • Test that the install fails when the lockfile hash does not match the artefact, and that the signature-verification step runs in CI
  • Verify CSP headers and SRI attributes are working correctly - a modified CDN file should be blocked, and the unmodified one should still load (a resource that never loads usually means the crossorigin attribute is missing)
  • Test with dependency scanning tools to confirm no high-risk dependencies
  • Re-scan with security scanner to confirm the issue is resolved

Common Vulnerable Patterns

// VULNERABLE - pseudo-code
remote_code = fetch(untrusted_url)
execute(remote_code)
// Attack: untrusted_url is attacker-controlled or the response isn't verified
// Result: arbitrary code from the untrusted source runs with the application's full privileges

Why this is vulnerable: The application fetches code from an external source and runs it without checking where it came from or whether the response was tampered with. Anyone who controls that source chooses what the application executes, with no validation or sandboxing in the way.

Secure Patterns

// SECURE - pseudo-code
declare functionality = import_from_trusted_repository(name, version, hash_from_committed_lockfile)
// the expected hash lives in your repository, so a tampered artefact fails the install
// install with the lockfile-enforcing command, not the one that re-resolves versions
use(functionality)

Why this works:

  • Imports come from trusted package repositories (PyPI, npm, Maven Central) rather than arbitrary URLs or user-controlled sources
  • Each artefact is checked against a hash committed to your own repository, so a tampered download fails the install - and outside NuGet, publisher-signature verification is a separate step you have to run rather than something the install command performs
  • No dynamic code execution (exec, eval) on untrusted input or network-fetched code
  • Dependency scanning and vulnerability monitoring become possible, through a Software Bill of Materials (SBOM)

Additional Resources