assign-gingerly 0.0.75 → 0.0.77
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DX/installForwarding.js +1 -1
- package/DX/installForwarding.ts +1 -1
- package/README.md +2 -0
- package/assignFeatures.js +10 -31
- package/assignFeatures.ts +15 -39
- package/assignFrom.js +18 -17
- package/assignFrom.ts +23 -23
- package/assignFromAsync.js +14 -13
- package/assignFromAsync.ts +15 -15
- package/assignGingerly.js +87 -48
- package/assignGingerly.ts +177 -131
- package/assignPermissions/PermissionProcessor.js +166 -0
- package/assignPermissions/PermissionProcessor.ts +210 -0
- package/assignPermissions/isAllowedImportPath.js +2 -6
- package/assignPermissions/isAllowedImportPath.ts +3 -5
- package/assignPermissions/isAllowedUrl.js +18 -0
- package/assignPermissions/isAllowedUrl.ts +15 -0
- package/assignTentatively.js +9 -11
- package/assignTentatively.ts +11 -13
- package/beVigilant.js +1 -1
- package/beVigilant.ts +1 -1
- package/buildCSSQuery.js +1 -1
- package/buildCSSQuery.ts +1 -1
- package/eachTime.js +5 -6
- package/eachTime.ts +12 -15
- package/enhanceAll.js +3 -3
- package/enhanceAll.ts +4 -4
- package/evaluatePathWithAsyncMethods.js +12 -8
- package/evaluatePathWithAsyncMethods.ts +129 -117
- package/handlers/addEventListener.js +11 -11
- package/handlers/addEventListener.ts +12 -13
- package/handlers/lazyLoad.ts +7 -7
- package/handlers/lazyLoadSwitch.ts +3 -3
- package/handlers/manageTemplateList.js +10 -10
- package/handlers/manageTemplateList.ts +12 -12
- package/handlers/rangeSelector.ts +3 -3
- package/index.js +2 -3
- package/index.ts +2 -3
- package/inferencer/types/assign-gingerly/types.d.ts +40 -9
- package/inferencer/types/el-maker/types.d.ts +33 -0
- package/inferencer/types/scratch-box/types.d.ts +3 -0
- package/inferencer/types/truth-sourcer/types.d.ts +4 -0
- package/inferencer/types/wc-info/SimpleWCInfo.d.ts +15 -0
- package/package.json +24 -23
- package/parseWithAttrs.js +1 -1
- package/parseWithAttrs.ts +1 -1
- package/processHandlerCommands.js +5 -7
- package/processHandlerCommands.ts +12 -15
- package/{getValues.js → resolve/getValues.js} +19 -8
- package/{getValues.ts → resolve/getValues.ts} +331 -314
- package/{resolveIdRef.js → resolve/resolveIdRef.js} +1 -1
- package/{resolveIdRef.ts → resolve/resolveIdRef.ts} +1 -1
- package/{resolveValues.ts → resolve/resolveValues.ts} +1 -1
- package/types/assign-gingerly/types.d.ts +40 -9
- package/assignPermissions/restrictedProps.js +0 -43
- package/assignPermissions/restrictedProps.ts +0 -53
- package/resolveAndAssignFeatures.js +0 -36
- package/resolveAndAssignFeatures.ts +0 -43
- /package/{resolveTemplate.js → resolve/resolveTemplate.js} +0 -0
- /package/{resolveTemplate.ts → resolve/resolveTemplate.ts} +0 -0
- /package/{resolveValues.js → resolve/resolveValues.js} +0 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { isAllowedUrl } from './isAllowedUrl.js';
|
|
2
|
+
export class PermissionProcessor {
|
|
3
|
+
constructor(permissions) {
|
|
4
|
+
this.permissions = permissions;
|
|
5
|
+
const { props, attrs } = buildMaps(permissions);
|
|
6
|
+
this.props = props;
|
|
7
|
+
this.attrs = attrs;
|
|
8
|
+
this.methods = buildMethodSet(permissions);
|
|
9
|
+
this.warned = new Set();
|
|
10
|
+
this.warnedMethods = new Set();
|
|
11
|
+
}
|
|
12
|
+
get crossDomainImports() {
|
|
13
|
+
return !!this.permissions?.crossDomainImports;
|
|
14
|
+
}
|
|
15
|
+
get hasProps() {
|
|
16
|
+
return this.props.size > 0;
|
|
17
|
+
}
|
|
18
|
+
get hasAttrs() {
|
|
19
|
+
return this.attrs.size > 0;
|
|
20
|
+
}
|
|
21
|
+
checkRestrictedProp(key) {
|
|
22
|
+
if (!this.props.has(key))
|
|
23
|
+
return false;
|
|
24
|
+
this.warnRestricted(key);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
checkRestrictedMethod(methodName) {
|
|
28
|
+
if (!this.methods.has(methodName))
|
|
29
|
+
return false;
|
|
30
|
+
this.warnRestrictedMethod(methodName);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
redirectRestrictedProp(target, key, value) {
|
|
34
|
+
if (!this.props.has(key))
|
|
35
|
+
return false;
|
|
36
|
+
const setting = this.props.get(key);
|
|
37
|
+
const { allowCrossDomain, allowFromSameDomain } = setting || {};
|
|
38
|
+
if (allowCrossDomain) {
|
|
39
|
+
return this.applyAllowedSetting(setting, target, key, value);
|
|
40
|
+
}
|
|
41
|
+
if (allowFromSameDomain) {
|
|
42
|
+
if (isAllowedUrl(value)) {
|
|
43
|
+
return this.applyAllowedSetting(setting, target, key, value);
|
|
44
|
+
}
|
|
45
|
+
this.warnRestricted(key);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
if (setting?.useMethod) {
|
|
49
|
+
const method = target?.[setting.useMethod];
|
|
50
|
+
if (typeof method === 'function') {
|
|
51
|
+
method.call(target, value);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
this.warnRestricted(key);
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
checkRestrictedAttributeCall(methodName, args) {
|
|
59
|
+
if (methodName !== 'setAttribute' || args.length === 0 || this.attrs.size === 0) {
|
|
60
|
+
return { blocked: false };
|
|
61
|
+
}
|
|
62
|
+
const attrName = String(args[0]);
|
|
63
|
+
if (!this.attrs.has(attrName)) {
|
|
64
|
+
return { blocked: false };
|
|
65
|
+
}
|
|
66
|
+
const setting = this.attrs.get(attrName);
|
|
67
|
+
const value = args[1];
|
|
68
|
+
const { allowCrossDomain, allowFromSameDomain } = setting || {};
|
|
69
|
+
if (allowCrossDomain) {
|
|
70
|
+
return { blocked: false, attrName };
|
|
71
|
+
}
|
|
72
|
+
if (allowFromSameDomain) {
|
|
73
|
+
if (isAllowedUrl(value)) {
|
|
74
|
+
return { blocked: false, attrName };
|
|
75
|
+
}
|
|
76
|
+
this.warnRestricted(attrName);
|
|
77
|
+
return { blocked: true, attrName };
|
|
78
|
+
}
|
|
79
|
+
this.warnRestricted(attrName);
|
|
80
|
+
return { blocked: true, attrName };
|
|
81
|
+
}
|
|
82
|
+
applyAllowedSetting(setting, target, key, value) {
|
|
83
|
+
if (setting?.useMethod) {
|
|
84
|
+
const method = target?.[setting.useMethod];
|
|
85
|
+
if (typeof method === 'function') {
|
|
86
|
+
method.call(target, value);
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
this.warnRestricted(key);
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
warnRestricted(key) {
|
|
95
|
+
if (!this.warned.has(key)) {
|
|
96
|
+
this.warned.add(key);
|
|
97
|
+
console.warn(`assignGingerly: property '${key}' is in restrictedPropSettings — assignment skipped.`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
warnRestrictedMethod(methodName) {
|
|
101
|
+
if (!this.warnedMethods.has(methodName)) {
|
|
102
|
+
this.warnedMethods.add(methodName);
|
|
103
|
+
console.warn(`assignGingerly: method '${methodName}' is in restrictedMethodSettings — method call skipped.`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function buildMaps(permissions) {
|
|
108
|
+
const settings = permissions?.restrictedPropSettings;
|
|
109
|
+
const props = new Map();
|
|
110
|
+
const attrs = new Map();
|
|
111
|
+
if (!settings || settings.length === 0) {
|
|
112
|
+
return { props, attrs };
|
|
113
|
+
}
|
|
114
|
+
for (const setting of settings) {
|
|
115
|
+
if (typeof setting === 'string') {
|
|
116
|
+
if (props.has(setting)) {
|
|
117
|
+
throw new Error(`assignGingerly: duplicate restrictedPropSettings entry for '${setting}'.`);
|
|
118
|
+
}
|
|
119
|
+
props.set(setting, undefined);
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const propNames = normalizeStrings(setting.props);
|
|
123
|
+
if (propNames.length === 0) {
|
|
124
|
+
throw new Error('assignGingerly: restrictedPropSettings object entry must have a props value.');
|
|
125
|
+
}
|
|
126
|
+
for (const prop of propNames) {
|
|
127
|
+
if (props.has(prop)) {
|
|
128
|
+
throw new Error(`assignGingerly: duplicate restrictedPropSettings entry for '${prop}'.`);
|
|
129
|
+
}
|
|
130
|
+
props.set(prop, setting);
|
|
131
|
+
}
|
|
132
|
+
const attrNames = normalizeAttrNames(setting.attr, propNames);
|
|
133
|
+
for (const attr of attrNames) {
|
|
134
|
+
if (attrs.has(attr)) {
|
|
135
|
+
throw new Error(`assignGingerly: duplicate restrictedPropSettings attr entry for '${attr}'.`);
|
|
136
|
+
}
|
|
137
|
+
attrs.set(attr, setting);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return { props, attrs };
|
|
141
|
+
}
|
|
142
|
+
function normalizeStrings(value) {
|
|
143
|
+
if (value === undefined)
|
|
144
|
+
return [];
|
|
145
|
+
return Array.isArray(value) ? value : [value];
|
|
146
|
+
}
|
|
147
|
+
function normalizeAttrNames(attr, propNames) {
|
|
148
|
+
if (attr === undefined || attr === false)
|
|
149
|
+
return [];
|
|
150
|
+
if (attr === true)
|
|
151
|
+
return propNames;
|
|
152
|
+
return normalizeStrings(attr);
|
|
153
|
+
}
|
|
154
|
+
function buildMethodSet(permissions) {
|
|
155
|
+
const methodSettings = permissions?.restrictedMethodSettings;
|
|
156
|
+
const methods = new Set();
|
|
157
|
+
if (!methodSettings || methodSettings.length === 0) {
|
|
158
|
+
return methods;
|
|
159
|
+
}
|
|
160
|
+
for (const setting of methodSettings) {
|
|
161
|
+
if (typeof setting === 'string') {
|
|
162
|
+
methods.add(setting);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return methods;
|
|
166
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import type { AssignPermissions, RestrictedPropSetting } from '../types/assign-gingerly/types.js';
|
|
2
|
+
import { isAllowedUrl } from './isAllowedUrl.js';
|
|
3
|
+
|
|
4
|
+
export interface RestrictedPropSettingsMap {
|
|
5
|
+
props: Map<string, RestrictedPropSetting | undefined>;
|
|
6
|
+
attrs: Map<string, RestrictedPropSetting | undefined>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class PermissionProcessor {
|
|
10
|
+
private readonly permissions: AssignPermissions | undefined;
|
|
11
|
+
private readonly props: Map<string, RestrictedPropSetting | undefined>;
|
|
12
|
+
private readonly attrs: Map<string, RestrictedPropSetting | undefined>;
|
|
13
|
+
private readonly methods: Set<string>;
|
|
14
|
+
private readonly warned = new Set<string>();
|
|
15
|
+
private readonly warnedMethods = new Set<string>();
|
|
16
|
+
|
|
17
|
+
constructor(permissions: AssignPermissions | undefined) {
|
|
18
|
+
this.permissions = permissions;
|
|
19
|
+
const { props, attrs } = buildMaps(permissions);
|
|
20
|
+
this.props = props;
|
|
21
|
+
this.attrs = attrs;
|
|
22
|
+
this.methods = buildMethodSet(permissions);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get crossDomainImports(): boolean {
|
|
26
|
+
return !!this.permissions?.crossDomainImports;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get hasProps(): boolean {
|
|
30
|
+
return this.props.size > 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get hasAttrs(): boolean {
|
|
34
|
+
return this.attrs.size > 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
checkRestrictedProp(key: string): boolean {
|
|
38
|
+
if (!this.props.has(key)) return false;
|
|
39
|
+
this.warnRestricted(key);
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
checkRestrictedMethod(methodName: string): boolean {
|
|
44
|
+
if (!this.methods.has(methodName)) return false;
|
|
45
|
+
this.warnRestrictedMethod(methodName);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
redirectRestrictedProp(target: any, key: string, value: any): boolean {
|
|
50
|
+
if (!this.props.has(key)) return false;
|
|
51
|
+
const setting = this.props.get(key);
|
|
52
|
+
const { allowCrossDomain, allowFromSameDomain } = setting || {};
|
|
53
|
+
|
|
54
|
+
if (allowCrossDomain) {
|
|
55
|
+
return this.applyAllowedSetting(setting, target, key, value);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (allowFromSameDomain) {
|
|
59
|
+
if (isAllowedUrl(value)) {
|
|
60
|
+
return this.applyAllowedSetting(setting, target, key, value);
|
|
61
|
+
}
|
|
62
|
+
this.warnRestricted(key);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (setting?.useMethod) {
|
|
67
|
+
const method = target?.[setting.useMethod];
|
|
68
|
+
if (typeof method === 'function') {
|
|
69
|
+
method.call(target, value);
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
this.warnRestricted(key);
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
checkRestrictedAttributeCall(
|
|
79
|
+
methodName: string,
|
|
80
|
+
args: any[]
|
|
81
|
+
): { blocked: boolean; attrName?: string } {
|
|
82
|
+
if (methodName !== 'setAttribute' || args.length === 0 || this.attrs.size === 0) {
|
|
83
|
+
return { blocked: false };
|
|
84
|
+
}
|
|
85
|
+
const attrName = String(args[0]);
|
|
86
|
+
if (!this.attrs.has(attrName)) {
|
|
87
|
+
return { blocked: false };
|
|
88
|
+
}
|
|
89
|
+
const setting = this.attrs.get(attrName);
|
|
90
|
+
const value = args[1];
|
|
91
|
+
const { allowCrossDomain, allowFromSameDomain } = setting || {};
|
|
92
|
+
|
|
93
|
+
if (allowCrossDomain) {
|
|
94
|
+
return { blocked: false, attrName };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (allowFromSameDomain) {
|
|
98
|
+
if (isAllowedUrl(value)) {
|
|
99
|
+
return { blocked: false, attrName };
|
|
100
|
+
}
|
|
101
|
+
this.warnRestricted(attrName);
|
|
102
|
+
return { blocked: true, attrName };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
this.warnRestricted(attrName);
|
|
106
|
+
return { blocked: true, attrName };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private applyAllowedSetting(
|
|
110
|
+
setting: RestrictedPropSetting | undefined,
|
|
111
|
+
target: any,
|
|
112
|
+
key: string,
|
|
113
|
+
value: any
|
|
114
|
+
): boolean {
|
|
115
|
+
if (setting?.useMethod) {
|
|
116
|
+
const method = target?.[setting.useMethod];
|
|
117
|
+
if (typeof method === 'function') {
|
|
118
|
+
method.call(target, value);
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
this.warnRestricted(key);
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private warnRestricted(key: string): void {
|
|
128
|
+
if (!this.warned.has(key)) {
|
|
129
|
+
this.warned.add(key);
|
|
130
|
+
console.warn(`assignGingerly: property '${key}' is in restrictedPropSettings — assignment skipped.`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private warnRestrictedMethod(methodName: string): void {
|
|
135
|
+
if (!this.warnedMethods.has(methodName)) {
|
|
136
|
+
this.warnedMethods.add(methodName);
|
|
137
|
+
console.warn(`assignGingerly: method '${methodName}' is in restrictedMethodSettings — method call skipped.`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function buildMaps(permissions: AssignPermissions | undefined): RestrictedPropSettingsMap {
|
|
143
|
+
const settings = permissions?.restrictedPropSettings;
|
|
144
|
+
const props = new Map<string, RestrictedPropSetting | undefined>();
|
|
145
|
+
const attrs = new Map<string, RestrictedPropSetting | undefined>();
|
|
146
|
+
if (!settings || settings.length === 0) {
|
|
147
|
+
return { props, attrs };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
for (const setting of settings) {
|
|
151
|
+
if (typeof setting === 'string') {
|
|
152
|
+
if (props.has(setting)) {
|
|
153
|
+
throw new Error(`assignGingerly: duplicate restrictedPropSettings entry for '${setting}'.`);
|
|
154
|
+
}
|
|
155
|
+
props.set(setting, undefined);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const propNames = normalizeStrings(setting.props);
|
|
160
|
+
if (propNames.length === 0) {
|
|
161
|
+
throw new Error('assignGingerly: restrictedPropSettings object entry must have a props value.');
|
|
162
|
+
}
|
|
163
|
+
for (const prop of propNames) {
|
|
164
|
+
if (props.has(prop)) {
|
|
165
|
+
throw new Error(`assignGingerly: duplicate restrictedPropSettings entry for '${prop}'.`);
|
|
166
|
+
}
|
|
167
|
+
props.set(prop, setting);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const attrNames = normalizeAttrNames(setting.attr, propNames);
|
|
171
|
+
for (const attr of attrNames) {
|
|
172
|
+
if (attrs.has(attr)) {
|
|
173
|
+
throw new Error(`assignGingerly: duplicate restrictedPropSettings attr entry for '${attr}'.`);
|
|
174
|
+
}
|
|
175
|
+
attrs.set(attr, setting);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return { props, attrs };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function normalizeStrings(value: string | string[] | undefined): string[] {
|
|
182
|
+
if (value === undefined) return [];
|
|
183
|
+
return Array.isArray(value) ? value : [value];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function normalizeAttrNames(
|
|
187
|
+
attr: boolean | string | string[] | undefined,
|
|
188
|
+
propNames: string[]
|
|
189
|
+
): string[] {
|
|
190
|
+
if (attr === undefined || attr === false) return [];
|
|
191
|
+
if (attr === true) return propNames;
|
|
192
|
+
return normalizeStrings(attr);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function buildMethodSet(permissions: AssignPermissions | undefined): Set<string> {
|
|
196
|
+
const methodSettings = permissions?.restrictedMethodSettings;
|
|
197
|
+
const methods = new Set<string>();
|
|
198
|
+
if (!methodSettings || methodSettings.length === 0) {
|
|
199
|
+
return methods;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
for (const setting of methodSettings) {
|
|
203
|
+
if (typeof setting === 'string') {
|
|
204
|
+
methods.add(setting);
|
|
205
|
+
}
|
|
206
|
+
// Phase II: object-form RestrictedMethodConfig entries are ignored for now.
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return methods;
|
|
210
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isAllowedUrl } from './isAllowedUrl.js';
|
|
1
2
|
/**
|
|
2
3
|
* Check whether an import specifier resolves to the current origin or is
|
|
3
4
|
* covered by an import-map entry.
|
|
@@ -8,12 +9,7 @@ export function isAllowedImportPath(value) {
|
|
|
8
9
|
if (!value || !(value.startsWith('./') || value.startsWith('../') || value.startsWith('/'))) {
|
|
9
10
|
return isImportMapSpecifier(value);
|
|
10
11
|
}
|
|
11
|
-
|
|
12
|
-
return new URL(value, document.baseURI).origin === location.origin;
|
|
13
|
-
}
|
|
14
|
-
catch {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
12
|
+
return isAllowedUrl(value);
|
|
17
13
|
}
|
|
18
14
|
function isImportMapSpecifier(value) {
|
|
19
15
|
const importMaps = document.querySelectorAll('script[type="importmap"]');
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isAllowedUrl } from './isAllowedUrl.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Check whether an import specifier resolves to the current origin or is
|
|
3
5
|
* covered by an import-map entry.
|
|
@@ -8,11 +10,7 @@ export function isAllowedImportPath(value: string): boolean {
|
|
|
8
10
|
return isImportMapSpecifier(value);
|
|
9
11
|
}
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
return new URL(value, document.baseURI).origin === location.origin;
|
|
13
|
-
} catch {
|
|
14
|
-
return false;
|
|
15
|
-
}
|
|
13
|
+
return isAllowedUrl(value);
|
|
16
14
|
}
|
|
17
15
|
|
|
18
16
|
function isImportMapSpecifier(value: string): boolean {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check whether a URL string resolves to the current origin.
|
|
3
|
+
*
|
|
4
|
+
* Returns false for non-strings, empty strings, malformed URLs, or
|
|
5
|
+
* any URL whose origin differs from `location.origin`.
|
|
6
|
+
*/
|
|
7
|
+
export function isAllowedUrl(value, base = document.baseURI) {
|
|
8
|
+
if (typeof document === 'undefined' || typeof location === 'undefined')
|
|
9
|
+
return false;
|
|
10
|
+
if (typeof value !== 'string' || value === '')
|
|
11
|
+
return false;
|
|
12
|
+
try {
|
|
13
|
+
return new URL(value, base).origin === location.origin;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check whether a URL string resolves to the current origin.
|
|
3
|
+
*
|
|
4
|
+
* Returns false for non-strings, empty strings, malformed URLs, or
|
|
5
|
+
* any URL whose origin differs from `location.origin`.
|
|
6
|
+
*/
|
|
7
|
+
export function isAllowedUrl(value: string, base = document.baseURI): boolean {
|
|
8
|
+
if (typeof document === 'undefined' || typeof location === 'undefined') return false;
|
|
9
|
+
if (typeof value !== 'string' || value === '') return false;
|
|
10
|
+
try {
|
|
11
|
+
return new URL(value, base).origin === location.origin;
|
|
12
|
+
} catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
package/assignTentatively.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { buildRestrictedPropSet, checkRestrictedProp } from './assignPermissions/restrictedProps.js';
|
|
2
1
|
/**
|
|
3
2
|
* Helper function to check if a string key represents an += command
|
|
4
3
|
*/
|
|
@@ -114,13 +113,12 @@ function getTopLevelKey(path) {
|
|
|
114
113
|
/**
|
|
115
114
|
* Main assignTentatively function with reversal support
|
|
116
115
|
*/
|
|
117
|
-
export function assignTentatively(target, source, options,
|
|
116
|
+
export function assignTentatively(target, source, options, permissionProcessor) {
|
|
118
117
|
if (!target || typeof target !== 'object') {
|
|
119
118
|
return target;
|
|
120
119
|
}
|
|
121
120
|
const reversal = options?.reversal || {};
|
|
122
121
|
const trackedCreatedPaths = new Set();
|
|
123
|
-
const restrictedPropSet = buildRestrictedPropSet(permissions);
|
|
124
122
|
// Process all keys from source
|
|
125
123
|
for (const key of Object.keys(source)) {
|
|
126
124
|
const value = source[key];
|
|
@@ -132,7 +130,7 @@ export function assignTentatively(target, source, options, permissions) {
|
|
|
132
130
|
const pathParts = parsePath(path);
|
|
133
131
|
const topLevelKey = pathParts[0];
|
|
134
132
|
const lastKey = pathParts[pathParts.length - 1];
|
|
135
|
-
if (checkRestrictedProp(
|
|
133
|
+
if (permissionProcessor?.checkRestrictedProp(lastKey)) {
|
|
136
134
|
continue;
|
|
137
135
|
}
|
|
138
136
|
// Track if we created a new top-level path (BEFORE calling ensureNestedPath)
|
|
@@ -155,7 +153,7 @@ export function assignTentatively(target, source, options, permissions) {
|
|
|
155
153
|
}
|
|
156
154
|
else {
|
|
157
155
|
// Plain key - direct operation on target
|
|
158
|
-
if (checkRestrictedProp(
|
|
156
|
+
if (permissionProcessor?.checkRestrictedProp(path)) {
|
|
159
157
|
continue;
|
|
160
158
|
}
|
|
161
159
|
if (path in target) {
|
|
@@ -183,7 +181,7 @@ export function assignTentatively(target, source, options, permissions) {
|
|
|
183
181
|
lhsPathParts = parsePath(lhsPath);
|
|
184
182
|
const topLevelKey = lhsPathParts[0];
|
|
185
183
|
lhsLastKey = lhsPathParts[lhsPathParts.length - 1];
|
|
186
|
-
if (checkRestrictedProp(
|
|
184
|
+
if (permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
|
|
187
185
|
continue;
|
|
188
186
|
}
|
|
189
187
|
// Track if we created a new top-level path (BEFORE calling ensureNestedPath)
|
|
@@ -195,7 +193,7 @@ export function assignTentatively(target, source, options, permissions) {
|
|
|
195
193
|
else {
|
|
196
194
|
lhsPathParts = [lhsPath];
|
|
197
195
|
lhsLastKey = lhsPath;
|
|
198
|
-
if (checkRestrictedProp(
|
|
196
|
+
if (permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
|
|
199
197
|
continue;
|
|
200
198
|
}
|
|
201
199
|
lhsParent = target;
|
|
@@ -301,7 +299,7 @@ export function assignTentatively(target, source, options, permissions) {
|
|
|
301
299
|
const pathParts = parsePath(key);
|
|
302
300
|
const topLevelKey = pathParts[0];
|
|
303
301
|
const lastKey = pathParts[pathParts.length - 1];
|
|
304
|
-
if (checkRestrictedProp(
|
|
302
|
+
if (permissionProcessor?.checkRestrictedProp(lastKey)) {
|
|
305
303
|
continue;
|
|
306
304
|
}
|
|
307
305
|
// Track if we created a new top-level path (BEFORE calling ensureNestedPath)
|
|
@@ -323,7 +321,7 @@ export function assignTentatively(target, source, options, permissions) {
|
|
|
323
321
|
}
|
|
324
322
|
// For nested objects, recursively apply with nested reversal tracking
|
|
325
323
|
const nestedReversal = {};
|
|
326
|
-
assignTentatively(parent[lastKey], value, { reversal: nestedReversal },
|
|
324
|
+
assignTentatively(parent[lastKey], value, { reversal: nestedReversal }, permissionProcessor);
|
|
327
325
|
// Merge nested reversals
|
|
328
326
|
for (const revKey of Object.keys(nestedReversal)) {
|
|
329
327
|
if (!(revKey in reversal)) {
|
|
@@ -344,7 +342,7 @@ export function assignTentatively(target, source, options, permissions) {
|
|
|
344
342
|
}
|
|
345
343
|
else {
|
|
346
344
|
// Non-nested key
|
|
347
|
-
if (checkRestrictedProp(
|
|
345
|
+
if (permissionProcessor?.checkRestrictedProp(key)) {
|
|
348
346
|
continue;
|
|
349
347
|
}
|
|
350
348
|
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
@@ -359,7 +357,7 @@ export function assignTentatively(target, source, options, permissions) {
|
|
|
359
357
|
target[key] = {};
|
|
360
358
|
}
|
|
361
359
|
const nestedReversal = {};
|
|
362
|
-
assignTentatively(target[key], value, { reversal: nestedReversal },
|
|
360
|
+
assignTentatively(target[key], value, { reversal: nestedReversal }, permissionProcessor);
|
|
363
361
|
// Merge nested reversals
|
|
364
362
|
for (const revKey of Object.keys(nestedReversal)) {
|
|
365
363
|
if (!(revKey in reversal)) {
|
package/assignTentatively.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* assignTentatively — reversible assignment with change tracking.
|
|
3
3
|
*/
|
|
4
|
-
import type { IAssignTentativelyOptions } from './types/assign-gingerly/types.js';
|
|
5
|
-
import type { AssignPermissions } from './types/assign-gingerly/types.js';
|
|
6
|
-
import { buildRestrictedPropSet, checkRestrictedProp } from './assignPermissions/restrictedProps.js';
|
|
4
|
+
import type { IAssignTentativelyOptions, PermissionProcessor } from './types/assign-gingerly/types.js';
|
|
7
5
|
export type { IAssignTentativelyOptions };
|
|
8
6
|
|
|
9
7
|
/**
|
|
@@ -137,7 +135,7 @@ export function assignTentatively(
|
|
|
137
135
|
target: any,
|
|
138
136
|
source: Record<string | symbol, any>,
|
|
139
137
|
options?: IAssignTentativelyOptions,
|
|
140
|
-
|
|
138
|
+
permissionProcessor?: PermissionProcessor
|
|
141
139
|
): any {
|
|
142
140
|
if (!target || typeof target !== 'object') {
|
|
143
141
|
return target;
|
|
@@ -145,7 +143,7 @@ export function assignTentatively(
|
|
|
145
143
|
|
|
146
144
|
const reversal = options?.reversal || {};
|
|
147
145
|
const trackedCreatedPaths = new Set<string>();
|
|
148
|
-
|
|
146
|
+
|
|
149
147
|
|
|
150
148
|
// Process all keys from source
|
|
151
149
|
for (const key of Object.keys(source)) {
|
|
@@ -160,7 +158,7 @@ export function assignTentatively(
|
|
|
160
158
|
const topLevelKey = pathParts[0];
|
|
161
159
|
const lastKey = pathParts[pathParts.length - 1];
|
|
162
160
|
|
|
163
|
-
if (checkRestrictedProp(
|
|
161
|
+
if (permissionProcessor?.checkRestrictedProp(lastKey)) {
|
|
164
162
|
continue;
|
|
165
163
|
}
|
|
166
164
|
|
|
@@ -184,7 +182,7 @@ export function assignTentatively(
|
|
|
184
182
|
}
|
|
185
183
|
} else {
|
|
186
184
|
// Plain key - direct operation on target
|
|
187
|
-
if (checkRestrictedProp(
|
|
185
|
+
if (permissionProcessor?.checkRestrictedProp(path)) {
|
|
188
186
|
continue;
|
|
189
187
|
}
|
|
190
188
|
if (path in target) {
|
|
@@ -215,7 +213,7 @@ export function assignTentatively(
|
|
|
215
213
|
const topLevelKey = lhsPathParts[0];
|
|
216
214
|
lhsLastKey = lhsPathParts[lhsPathParts.length - 1];
|
|
217
215
|
|
|
218
|
-
if (checkRestrictedProp(
|
|
216
|
+
if (permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
|
|
219
217
|
continue;
|
|
220
218
|
}
|
|
221
219
|
|
|
@@ -228,7 +226,7 @@ export function assignTentatively(
|
|
|
228
226
|
} else {
|
|
229
227
|
lhsPathParts = [lhsPath];
|
|
230
228
|
lhsLastKey = lhsPath;
|
|
231
|
-
if (checkRestrictedProp(
|
|
229
|
+
if (permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
|
|
232
230
|
continue;
|
|
233
231
|
}
|
|
234
232
|
lhsParent = target;
|
|
@@ -337,7 +335,7 @@ export function assignTentatively(
|
|
|
337
335
|
const topLevelKey = pathParts[0];
|
|
338
336
|
const lastKey = pathParts[pathParts.length - 1];
|
|
339
337
|
|
|
340
|
-
if (checkRestrictedProp(
|
|
338
|
+
if (permissionProcessor?.checkRestrictedProp(lastKey)) {
|
|
341
339
|
continue;
|
|
342
340
|
}
|
|
343
341
|
|
|
@@ -362,7 +360,7 @@ export function assignTentatively(
|
|
|
362
360
|
}
|
|
363
361
|
// For nested objects, recursively apply with nested reversal tracking
|
|
364
362
|
const nestedReversal: Record<string | symbol, any> = {};
|
|
365
|
-
assignTentatively(parent[lastKey], value, { reversal: nestedReversal },
|
|
363
|
+
assignTentatively(parent[lastKey], value, { reversal: nestedReversal }, permissionProcessor);
|
|
366
364
|
// Merge nested reversals
|
|
367
365
|
for (const revKey of Object.keys(nestedReversal)) {
|
|
368
366
|
if (!(revKey in reversal)) {
|
|
@@ -381,7 +379,7 @@ export function assignTentatively(
|
|
|
381
379
|
}
|
|
382
380
|
} else {
|
|
383
381
|
// Non-nested key
|
|
384
|
-
if (checkRestrictedProp(
|
|
382
|
+
if (permissionProcessor?.checkRestrictedProp(key)) {
|
|
385
383
|
continue;
|
|
386
384
|
}
|
|
387
385
|
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
@@ -396,7 +394,7 @@ export function assignTentatively(
|
|
|
396
394
|
target[key] = {};
|
|
397
395
|
}
|
|
398
396
|
const nestedReversal: Record<string | symbol, any> = {};
|
|
399
|
-
assignTentatively(target[key], value, { reversal: nestedReversal },
|
|
397
|
+
assignTentatively(target[key], value, { reversal: nestedReversal }, permissionProcessor);
|
|
400
398
|
// Merge nested reversals
|
|
401
399
|
for (const revKey of Object.keys(nestedReversal)) {
|
|
402
400
|
if (!(revKey in reversal)) {
|
package/beVigilant.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { Infer } from './inferencer/inferencer.js';
|
|
11
11
|
import { withScopePerimeter } from './inferencer/withScopePerimeter.js';
|
|
12
|
-
import { registerInCache } from './resolveIdRef.js';
|
|
12
|
+
import { registerInCache } from './resolve/resolveIdRef.js';
|
|
13
13
|
/**
|
|
14
14
|
* Set up a MutationObserver that watches for new [itemprop] elements
|
|
15
15
|
* and applies inferred assignments from the live `from` object.
|
package/beVigilant.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import { Infer } from './inferencer/inferencer.js';
|
|
12
12
|
import { withScopePerimeter } from './inferencer/withScopePerimeter.js';
|
|
13
|
-
import { registerInCache } from './resolveIdRef.js';
|
|
13
|
+
import { registerInCache } from './resolve/resolveIdRef.js';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Set up a MutationObserver that watches for new [itemprop] elements
|
package/buildCSSQuery.js
CHANGED
package/buildCSSQuery.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EnhancementConfig, AttrPatterns } from './types/assign-gingerly/types';
|
|
2
|
-
import { resolveTemplate } from './resolveTemplate.js';
|
|
2
|
+
import { resolveTemplate } from './resolve/resolveTemplate.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Extracts attribute names from withAttrs configuration
|