What is TrustedScriptURL?
TrustedScriptURL is a critical interface within the Trusted Types API that provides developers with a powerful mechanism to prevent cross-site scripting (XSS) attacks when loading external scripts. By ensuring that script URLs must pass through a developer-defined policy before being executed, TrustedScriptURL creates a robust defense against one of the most prevalent security vulnerabilities in web applications.
This interface represents a string that the browser can safely parse as a URL of an external script, with the guarantee that the URL has been validated and sanitized according to security policies defined by the developer. The value of a TrustedScriptURL object is immutable once created, meaning JavaScript cannot modify it after creation, providing a strong guarantee of security throughout the object's lifecycle.
TrustedScriptURL is one of three trusted type interfaces defined by the Trusted Types API, alongside TrustedHTML for HTML injection sinks and TrustedScript for executable JavaScript. Together, these interfaces provide comprehensive protection against DOM-based XSS attacks by securing all major pathways through which untrusted data could be executed as code.
Key Features
- Immutable URLs: Once created, TrustedScriptURL objects cannot be modified, preventing tampering after validation
- Policy-Based Validation: All script URLs pass through developer-defined policies that can enforce domain whitelisting, protocol requirements, and path restrictions
- CSP Integration: Works seamlessly with Content Security Policy for enforcement
- Web Worker Support: Available across all execution contexts, including Web Workers
- Defense in Depth: Adds a robust layer of protection beyond traditional input validation
For developers building modern web applications that load scripts dynamically or integrate third-party JavaScript resources, understanding how to properly implement and use TrustedScriptURL is essential for maintaining robust security posture. When combined with comprehensive web development security practices, organizations can build resilient applications that protect against evolving threats.
Essential security benefits for modern web applications
XSS Prevention
Blocks attackers from injecting malicious scripts through URL manipulation by requiring all script URLs to pass through a policy before execution
Immutable Security
TrustedScriptURL objects cannot be changed after creation, ensuring consistent security guarantees throughout the object's lifecycle
Defense in Depth
Adds a robust layer of protection beyond traditional input validation, working alongside CSP for comprehensive security
Cross-Context Support
Available in both main thread and Web Workers for consistent security across all execution contexts
Creating TrustedScriptURL Objects
TrustedScriptURL objects cannot be instantiated directly using a constructor. Instead, they are created by calling the createScriptURL() method on a TrustedTypePolicy object. Developers first need to create a policy using the trustedTypes.createPolicy() method, which is available globally on both Window and Worker objects.
Creating a Policy
// Create a policy for trusted script URLs
const scriptPolicy = trustedTypes.createPolicy('scriptLoader', {
createScriptURL(input) {
const url = new URL(input, document.baseURI);
// Only allow HTTPS URLs from trusted domains
if (url.protocol !== 'https:') {
return '';
}
// Check against allowed domains
const allowedDomains = ['cdn.example.com', 'analytics.example.com'];
if (!allowedDomains.includes(url.hostname)) {
return '';
}
return url.toString();
}
});
The policy object serves as a factory for creating trusted type objects. When creating a policy for script URLs, developers define a createScriptURL() function that accepts a string URL and returns either a validated URL string or an empty string if the URL fails validation. This function is the security checkpoint through which all script URLs must pass.
The policy name passed to createPolicy() must be registered with the Content Security Policy through the trusted-types directive. This ensures that only policies defined by the application can create trusted types, preventing attackers from creating malicious policies that bypass security checks.
Policy Creation Best Practices
When designing policies for TrustedScriptURL, developers should follow several best practices to maximize security while maintaining functionality:
- Domain Whitelisting: Only allow script URLs from known, trusted domains to prevent loading scripts from malicious servers
- Protocol Restrictions: Require HTTPS for all script URLs to prevent man-in-the-middle attacks
- Path Validation: Consider restricting the paths from which scripts can be loaded, especially for CDN-hosted resources
- Logging and Monitoring: Implement logging within policies to detect and investigate suspicious script loading attempts
Implementing these security measures is a core component of secure web application development, helping organizations meet security compliance requirements and protect their users from emerging threats.
Methods of TrustedScriptURL
toString()
Returns a string containing the sanitized URL. This method enables TrustedScriptURL objects to be used seamlessly in places where strings are expected, such as when setting the src attribute of script elements:
const trustedUrl = scriptPolicy.createScriptURL('https://cdn.example.com/app.js');
// Using toString()
console.log(trustedUrl.toString());
// Output: "https://cdn.example.com/app.js"
// Direct string coercion also works
const urlString = String(trustedUrl);
// or
const urlString = `${trustedUrl}`;
The toString() method is particularly useful when debugging or logging script URLs, as it provides a human-readable representation of the trusted URL.
toJSON()
Returns a JSON representation of the stored URL data. This method enables TrustedScriptURL objects to be serialized to JSON format, which can be useful when logging, debugging, or transmitting trusted URLs through APIs that expect JSON input:
const trustedUrl = scriptPolicy.createScriptURL('https://cdn.example.com/app.js');
const jsonRepresentation = trustedUrl.toJSON();
console.log(jsonRepresentation);
// Output: "https://cdn.example.com/app.js"
console.log(JSON.stringify({ scriptUrl: trustedUrl }));
// Output: {"scriptUrl":"https://cdn.example.com/app.js"}
Both toString() and toJSON() return the same value--the URL string. The distinction lies in their intended use cases: toString() for general string operations and toJSON() for JSON serialization scenarios.
Integration with Content Security Policy
To fully leverage TrustedScriptURL and the broader Trusted Types API, developers should configure their Content Security Policy to enforce trusted type usage. This is achieved through the require-trusted-types-for CSP directive, which instructs the browser to reject any string assignments to protected injection sinks.
require-trusted-types-for Directive
Content-Security-Policy: require-trusted-types-for 'script'; trusted-types scriptLoader;
When this CSP header is present, the browser will throw a TypeError whenever code attempts to assign a raw string to a protected sink. In the case of require-trusted-types-for 'script', this protection applies to script element operations and other JavaScript-related sinks.
This enforcement mechanism is critical because it prevents accidental or malicious bypass of the trusted types system. Without CSP enforcement, code could still pass raw strings to script src attributes, defeating the purpose of using TrustedScriptURL.
trusted-types Directive
The trusted-types directive specifies which policy names are permitted to create trusted type objects. By explicitly listing allowed policy names, developers can prevent attackers from creating their own policies that might have weaker security checks:
Content-Security-Policy:
require-trusted-types-for 'script';
trusted-types scriptLoader analyticsPolicy default;
In this example, three policy names are permitted: 'scriptLoader', 'analyticsPolicy', and 'default'. The 'default' policy is a special name that can be used to create a fallback policy for handling legacy code that hasn't yet been updated to use explicit policies.
Default Policy for Gradual Migration
The Trusted Types API supports creating a policy named 'default', which serves as a fallback for handling string values that are passed to protected sinks before they can be migrated to use trusted types explicitly:
// Create a default policy that logs violations but allows the operation
trustedTypes.createPolicy('default', {
createScriptURL(input) {
console.warn('String passed to script sink - please migrate to use trusted types');
// Return the input to allow the operation for gradual migration
return input;
}
});
This feature enables gradual adoption of trusted types in existing codebases. However, when fully enforcing trusted types, the default policy should return null or empty string for any input that doesn't pass validation, which will cause the browser to throw a TypeError.
Content Security Policy is a fundamental component of modern web security, and understanding how to properly configure CSP headers is essential for any development team prioritizing application security.
Practical Implementation Examples
Loading Scripts from a Trusted CDN
One of the most common use cases for TrustedScriptURL is loading scripts from a trusted CDN. This pattern ensures that only scripts from the approved CDN domain can be loaded, preventing attackers from manipulating the CDN URL to load malicious code:
// Define a policy for CDN script loading
const cdnPolicy = trustedTypes.createPolicy('cdnScripts', {
createScriptURL(input) {
const url = new URL(input, document.baseURI);
// Only allow scripts from our approved CDN
if (url.hostname !== 'cdn.ourcompany.com') {
console.error('Rejected script URL from unauthorized domain:', url.hostname);
return '';
}
// Ensure HTTPS is used
if (url.protocol !== 'https:') {
console.error('Rejected non-HTTPS script URL');
return '';
}
// Verify the path matches expected patterns
if (!url.pathname.startsWith('/js/v2/')) {
console.error('Rejected script with unexpected path');
return '';
}
return url.toString();
}
});
// Safe script loading
function loadCdnScript(relativePath) {
const script = document.createElement('script');
script.src = cdnPolicy.createScriptURL(relativePath);
script.async = true;
document.head.appendChild(script);
}
// Usage
loadCdnScript('/js/v2/vendor/library.min.js');
loadCdnScript('/js/v2/app.bundle.js');
This implementation provides strong guarantees that only scripts from the approved CDN path will be loaded, protecting against various attack vectors including subdomain takeovers and URL manipulation.
Secure Third-Party Script Integration
Many web applications integrate third-party scripts for analytics, advertising, or functionality. TrustedScriptURL can help secure these integrations by ensuring only approved third-party URLs are loaded:
// Policy for third-party script integration
const thirdPartyPolicy = trustedTypes.createPolicy('thirdPartyScripts', {
createScriptURL(input) {
const approvedPartners = [
'google-analytics.com',
'facebook.net',
'intercom.io',
'hotjar.com'
];
const url = new URL(input, document.baseURI);
// Check if the domain is approved (including subdomains)
const isApproved = approvedPartners.some(domain =>
url.hostname === domain || url.hostname.endsWith('.' + domain)
);
if (!isApproved || url.protocol !== 'https:') {
return '';
}
return url.toString();
}
});
// Secure loader for third-party scripts
function loadThirdPartyScript(url, attributes = {}) {
const script = document.createElement('script');
script.src = thirdPartyPolicy.createScriptURL(url);
// Apply additional attributes
Object.entries(attributes).forEach(([key, value]) => {
script.setAttribute(key, value);
});
document.head.appendChild(script);
}
For teams implementing these security patterns, our web development services can provide guidance on integrating TrustedScriptURL into production applications while maintaining code quality and security standards.
Browser Support and Availability
Current Browser Support
TrustedScriptURL enjoys broad support across modern browsers:
| Browser | Support Status |
|---|---|
| Chrome | Full support, including CSP enforcement |
| Edge | Full support |
| Safari | Full support since version 15.4 |
| Firefox | Supported in recent releases |
The Trusted Types API is continuously evolving, and browser vendors are working to improve interoperability. Developers should consult current browser compatibility resources and consider feature detection when implementing trusted types in production applications.
Web Worker Support
A significant advantage of TrustedScriptURL is its availability in Web Workers. Since Web Workers operate in a separate global context and have different security considerations, having consistent trusted type support across both the main thread and worker threads is crucial for applications that use workers for background processing or parallel computation:
// In a Web Worker
const workerPolicy = trustedTypes.createPolicy('workerScripts', {
createScriptURL(url) {
// Validate URLs for worker script loading
if (isTrustedWorkerScript(url)) {
return url;
}
return '';
}
});
// Web Workers can create trusted type policies and use TrustedScriptURL
// objects just like the main thread, providing consistent security
// across all execution contexts in a web application.
Troubleshooting Common Issues
TypeError: Failed to Set the src Property
When require-trusted-types-for is enabled, passing a raw string to script.src throws a TypeError:
// This will throw an error when CSP is enforced
script.src = userProvidedUrl; // TypeError!
// Instead, use the policy
script.src = policy.createScriptURL(userProvidedUrl); // Works
This error indicates that the code is attempting to bypass the trusted types system by passing a raw string to a protected sink. The solution is to always use a policy to create trusted script URLs.
Policy Name Conflicts
When multiple scripts or modules attempt to create policies with the same name, only the first policy creation succeeds:
// Check if a policy already exists
if (!trustedTypes.getPolicy('sharedPolicy')) {
trustedTypes.createPolicy('sharedPolicy', {
createScriptURL(url) {
// Policy implementation
return url;
}
});
}
// Or use a unique naming convention
const moduleId = 'analyticsModule';
const policyName = `scriptPolicy_${moduleId}`;
if (!trustedTypes.getPolicy(policyName)) {
trustedTypes.createPolicy(policyName, {
createScriptURL(url) {
// Policy implementation
return url;
}
});
}
Empty String Returns
When a policy returns an empty string with strict CSP enforcement enabled, the browser throws a TypeError. This is the desired behavior for invalid or blocked URLs:
const policy = trustedTypes.createPolicy('strict', {
createScriptURL(input) {
if (!isValidUrl(input)) {
return ''; // Will cause TypeError when used
}
return input;
}
});
try {
script.src = policy.createScriptURL(invalidUrl);
} catch (e) {
console.error('Invalid script URL blocked by policy:', e.message);
// Handle the error appropriately
}
Security Best Practices
What is the primary purpose of TrustedScriptURL?
TrustedScriptURL prevents XSS attacks by ensuring all script URLs pass through a developer-defined policy before execution. It creates an immutable, validated representation of script URLs that cannot be tampered with after creation.
How is TrustedScriptURL different from regular strings?
Unlike strings, TrustedScriptURL objects are immutable (cannot be changed after creation) and can only be created through policies, ensuring all script URLs are explicitly validated before use.
Can TrustedScriptURL be used without CSP?
Yes, but without CSP enforcement with require-trusted-types-for, code could still bypass TrustedScriptURL by directly passing strings to script sinks. CSP provides complete enforcement.
What happens if a policy returns an empty string?
When strict CSP enforcement is enabled, an empty string return causes a TypeError, preventing the operation. This is the desired behavior for invalid or blocked URLs.