Skip to content

Commit 938bbe3

Browse files
committed
AG-40603 Add new api support provided from content-script
Squashed commit of the following: commit 5b57844 Author: Kurbanali Ruslan <[email protected]> Date: Tue Apr 8 10:22:54 2025 +0500 updated lock commit 8bfebef Merge: 2e6a527 51ddb4f Author: Kurbanali Ruslan <[email protected]> Date: Tue Apr 8 10:20:54 2025 +0500 Merge branch 'master' into enhance/AG-40603 commit 2e6a527 Merge: 0dc5ecf 83aa91b Author: Kurbanali Ruslan <[email protected]> Date: Fri Apr 4 18:14:04 2025 +0500 Merge branch 'master' into enhance/AG-40603 commit 0dc5ecf Author: Kurbanali Ruslan <[email protected]> Date: Fri Apr 4 10:56:03 2025 +0500 fixed changelog and bump version commit 9012aa7 Merge: e8cb691 8c03456 Author: Kurbanali Ruslan <[email protected]> Date: Thu Apr 3 15:11:28 2025 +0500 Merge branch 'master' into enhance/AG-40603 commit e8cb691 Author: Kurbanali Ruslan <[email protected]> Date: Thu Apr 3 15:11:24 2025 +0500 review changes commit 7336923 Author: Kurbanali Ruslan <[email protected]> Date: Wed Apr 2 19:26:23 2025 +0500 fixed failing test commit c61cfc4 Author: Kurbanali Ruslan <[email protected]> Date: Wed Apr 2 18:35:40 2025 +0500 changed formatting commit ecbbc41 Author: Kurbanali Ruslan <[email protected]> Date: Wed Apr 2 18:31:11 2025 +0500 updated docs commit c66923a Author: Kurbanali Ruslan <[email protected]> Date: Wed Apr 2 18:20:08 2025 +0500 reverted lock commit e716a4c Merge: 9878fbf 9d40d11 Author: Kurbanali Ruslan <[email protected]> Date: Wed Apr 2 14:58:51 2025 +0500 Merge branch 'master' into enhance/AG-40603 commit 9878fbf Author: Kurbanali Ruslan <[email protected]> Date: Wed Apr 2 14:56:11 2025 +0500 updated docs commit 95c3d97 Author: Kurbanali Ruslan <[email protected]> Date: Wed Apr 2 14:26:13 2025 +0500 added tests commit e61b5b3 Author: Kurbanali Ruslan <[email protected]> Date: Wed Mar 26 10:35:37 2025 +0500 added fixmes commit 25c1e08 Author: Kurbanali Ruslan <[email protected]> Date: Tue Mar 25 19:02:51 2025 +0500 added example usage commit bbfe00e Author: Kurbanali Ruslan <[email protected]> Date: Tue Mar 25 19:00:04 2025 +0500 added proper trusted types handling commit 61e686c Author: Kurbanali Ruslan <[email protected]> Date: Tue Mar 25 16:32:38 2025 +0500 added ChangeMethodReturnType utility type
1 parent 51ddb4f commit 938bbe3

13 files changed

+772
-46
lines changed

CHANGELOG.md

+7-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ The format is based on [Keep a Changelog], and this project adheres to [Semantic
1212

1313
## [Unreleased]
1414

15+
### Added
16+
17+
- TypeScript types for CoreLibs provided [`ContentScriptApi`](./README.md#scriptlets-api--content-script-api).
18+
- Trusted Types API utility - [`PolicyApi`](./README.md#scriptlets-api--content-script-api--policy-api).
19+
1520
### Changed
1621

1722
- Improved docs for `json-prune`, `xml-prune` and `trusted-prune-inbound-object` scriptlets [#392]
@@ -39,8 +44,8 @@ The format is based on [Keep a Changelog], and this project adheres to [Semantic
3944

4045
- issue with incorrectly removing content from parsed array when using the `json-prune` scriptlet [#482]
4146

42-
[v2.1.6]: https://github.com/AdguardTeam/Scriptlets/compare/v2.1.5...v2.1.6
43-
[#482]: https://github.com/AdguardTeam/Scriptlets/issues/482
47+
[v2.1.6]: https://github.com/AdguardTeam/Scriptlets/compare/v2.1.5...v2.1.6
48+
[#482]: https://github.com/AdguardTeam/Scriptlets/issues/482
4449

4550
## [v2.1.5] - 2025-02-28
4651

README.md

+145
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ AdGuard's Scriptlets and Redirect resources library which provides extended capa
2929
- [`getScriptletFunction()`](#scriptlets-api--getScriptletFunction)
3030
- [Properties](#scriptlets-api-properties)
3131
- [`SCRIPTLETS_VERSION`](#scriptlets-api--version)
32+
- [`ContentScriptApi`](#scriptlets-api--content-script-api)
33+
- [`PolicyApi`](#scriptlets-api--content-script-api--policy-api)
3234
- [Redirects API](#redirects-api)
3335
- [Redirects class](#redirects-api--redirects-class)
3436
- [`getRedirect()`](#redirects-api--getRedirect)
@@ -463,9 +465,19 @@ interface Source {
463465
* scriptlet can be called multiple times.
464466
*/
465467
uniqueId?: string;
468+
/**
469+
* Instance of content script provided API.
470+
*
471+
* Property optional because:
472+
* - for backwards compatibility.
473+
* - currently only CoreLibs provides this API.
474+
*/
475+
api?: ContentScriptApi;
466476
}
467477
```
468478

479+
see also [`ContentScriptApi`](#scriptlets-api--content-script-api) for more details.
480+
469481
#### <a name="scriptlets-api--getScriptletFunction"></a> `getScriptletFunction()`
470482

471483
```typescript
@@ -490,6 +502,139 @@ type: `string`
490502

491503
Current version of scriptlets library.
492504

505+
#### <a name="scriptlets-api--content-script-api"></a> `ContentScriptApi`
506+
507+
API provided by CoreLibs content script.
508+
509+
This API is used to provide a set of utilities and shared state for scriptlets
510+
running in the context of a web page. Particularly, it includes:
511+
512+
```typescript
513+
export interface ContentScriptApi {
514+
/**
515+
* Trusted Types Policy API utilities.
516+
*/
517+
readonly policy: PolicyApi;
518+
519+
/**
520+
* Shared state between different script and scriptlet rules.
521+
*
522+
* This object acts as a centralized repository for shared data.
523+
* - Keys represent the unique identifiers or names of the shared data.
524+
* - Values can be of any type and should correspond to the specific data shared across script rules.
525+
*
526+
* Example:.
527+
* ```adguard
528+
* ! Modify in one script rule
529+
* #%#api.shared.testKey = 'testValue'
530+
*
531+
* ! Access in another (logs 'testValue')
532+
* #%#console.log(api.shared.testKey)
533+
* ```
534+
*/
535+
readonly shared: Record<string, unknown>;
536+
}
537+
```
538+
539+
##### <a name="scriptlets-api--content-script-api--policy-api"></a> `PolicyApi`
540+
541+
Trusted Types Policy API utility.
542+
543+
This interface extends the native `TrustedTypePolicy` and `TrustedTypePolicyFactory`
544+
to provide a more user-friendly API for working with Trusted Types. In case if
545+
environment doesn't support Trusted Types API, it provides polyfilled methods
546+
and properties to ensure compatibility.
547+
548+
```typescript
549+
export interface PolicyApi extends TrustedTypePolicy, TrustedTypePolicyFactory {
550+
/**
551+
* Is Trusted Types API supported.
552+
*/
553+
isSupported: boolean;
554+
555+
/**
556+
* TrustedType enum attached to PolicyApi.
557+
*
558+
* Reason why we attach it to instance because inside
559+
* of script and scriptlet we can't import and to not
560+
* pollute global env with custom variables.
561+
*
562+
* @example
563+
* api.policy.TrustedType.HTML // "TrustedHTML"
564+
*/
565+
TrustedType: typeof TrustedType;
566+
567+
/**
568+
* Creates Trusted Type depending on `type`:
569+
* - `TrustedHTML`
570+
* - `TrustedScript`
571+
* - `TrustedScriptURL`
572+
* - or returns back `input` if none of them applicable.
573+
*
574+
* @example
575+
* divElement.innerHTML = api.policy.create(api.policy.TrustedType.HTML, '<div></div>');
576+
*
577+
* @param type Trusted Type.
578+
* @param input Input from which creates Trusted Type.
579+
* @returns Created value.
580+
*/
581+
create(type: TrustedType, input: string): string;
582+
583+
/**
584+
* Converts `value` of `attribute` into one of the Trusted Types:
585+
* - `TrustedHTML`
586+
* - `TrustedScript`
587+
* - `TrustedScriptURL`
588+
* - or returns back `value` if none of them applicable (`null`).
589+
*
590+
* @example
591+
* const trustedScriptURL = api.policy.convertAttributeToTrusted("script", "src", 'SOME_URL');
592+
* scriptElement.setAttribute("src", trustedScriptURL);
593+
*
594+
* @param tagName Name of an HTML tag.
595+
* @param attribute Attribute.
596+
* @param value Value of attribute that needs to be converted.
597+
* @param elementNS Element namespace, if empty defaults to the HTML namespace.
598+
* @param attrNS Attribute namespace, if empty defaults to null.
599+
* @returns Converted value.
600+
*/
601+
convertAttributeToTrusted(
602+
tagName: string,
603+
attribute: string,
604+
value: string,
605+
elementNS?: string,
606+
attrNS?: string,
607+
): string;
608+
609+
/**
610+
* Converts `value` of `property` into one of the Trusted Types:
611+
* - `TrustedHTML`
612+
* - `TrustedScript`
613+
* - `TrustedScriptURL`
614+
* - or returns back `value` if none of them applicable (`null`).
615+
*
616+
* @example
617+
* divElement.innerHTML = api.policy.convertPropertyToTrusted("div", "innerHTML", "<div></div>");
618+
*
619+
* @param tagName Name of an HTML tag.
620+
* @param property Property.
621+
* @param value Value or property.
622+
* @param elementNS Element namespace, if empty defaults to the HTML namespace.
623+
* @returns Converted value.
624+
*/
625+
convertPropertyToTrusted(
626+
tagName: string,
627+
property: string,
628+
value: string,
629+
elementNS?: string,
630+
): string;
631+
}
632+
```
633+
634+
- [TrustedTypePolicy](https://developer.mozilla.org/en-US/docs/Web/API/TrustedTypePolicy) interface
635+
- [TrustedTypePolicyFactory](https://developer.mozilla.org/en-US/docs/Web/API/TrustedTypePolicyFactory) interface
636+
637+
493638
### <a name="redirects-api"></a> Redirects API
494639

495640
You are welcome to use redirects as a CJS modules or ESM modules:

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@adguard/scriptlets",
3-
"version": "2.1.7",
3+
"version": "2.2.0",
44
"description": "AdGuard's JavaScript library of Scriptlets and Redirect resources",
55
"type": "module",
66
"scripts": {
@@ -89,6 +89,7 @@
8989
"@swc/core": "^1.8.0",
9090
"@types/js-yaml": "^3.12.10",
9191
"@types/node": "^22.7.9",
92+
"@types/trusted-types": "^2.0.7",
9293
"@typescript-eslint/eslint-plugin": "^5.52.0",
9394
"@typescript-eslint/parser": "^5.62.0",
9495
"axios": "^1.7.7",

pnpm-lock.yaml

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/helpers/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,5 @@ export * from './random-id';
3737
export * from './throttle';
3838
export * from './shadow-dom-utils';
3939
export * from './node-text-utils';
40+
export * from './trusted-types-utils';
4041
export * from './value-matchers';

src/helpers/node-text-utils.ts

+9-30
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,7 @@ import { nodeListToArray } from './array-utils';
33
import { getAddedNodes } from './observer';
44
import { toRegExp } from './string-utils';
55
import { type Source } from '../scriptlets';
6-
7-
declare global {
8-
interface Window {
9-
trustedTypes?: {
10-
createPolicy: (
11-
name: string,
12-
rules: { createScript: (input: string) => string }
13-
) => { createScript: (input: string) => string };
14-
};
15-
}
16-
}
6+
import { getTrustedTypesApi } from './trusted-types-utils';
177

188
type NodeHandler = (nodes: Node[]) => void;
199

@@ -121,28 +111,17 @@ export const replaceNodeText = (
121111
): void => {
122112
const { textContent } = node;
123113
if (textContent) {
114+
let modifiedText = textContent.replace(pattern, replacement);
115+
124116
// For websites that use Trusted Types
125117
// https://w3c.github.io/webappsec-trusted-types/dist/spec/
126-
if (
127-
node.nodeName === 'SCRIPT'
128-
&& window.trustedTypes
129-
&& window.trustedTypes.createPolicy
130-
) {
131-
// The name for the trusted-types policy should only be 'AGPolicy',because corelibs can
132-
// allow our policy if the server has restricted the creation of a trusted-types policy with
133-
// the directive 'Content-Security-Policy: trusted-types <policyName>;`.
134-
// If such a header is presented in the server response, corelibs adds permission to create
135-
// the 'AGPolicy' policy with the 'allow-duplicates' option to prevent errors.
136-
// See AG-18204 for details.
137-
const policy = window.trustedTypes.createPolicy('AGPolicy', {
138-
createScript: (string) => string,
139-
});
140-
const modifiedText = textContent.replace(pattern, replacement);
141-
const trustedReplacement = policy.createScript(modifiedText);
142-
node.textContent = trustedReplacement;
143-
} else {
144-
node.textContent = textContent.replace(pattern, replacement);
118+
if (node.nodeName === 'SCRIPT') {
119+
const trustedTypesApi = getTrustedTypesApi(source);
120+
modifiedText = trustedTypesApi.createScript(modifiedText);
145121
}
122+
123+
node.textContent = modifiedText;
124+
146125
hit(source);
147126
}
148127
};

0 commit comments

Comments
 (0)