@superbuilders/incept-renderer 0.1.13 → 0.1.15
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/dist/actions/index.js +5 -210
- package/dist/actions/index.js.map +1 -1
- package/dist/components/index.js +4 -213
- package/dist/components/index.js.map +1 -1
- package/dist/index.d.ts +23 -12
- package/dist/index.js +4 -213
- package/dist/index.js.map +1 -1
- package/dist/styles/duolingo.css +4 -0
- package/dist/styles/duolingo.css.map +1 -1
- package/dist/styles/themes.css +4 -0
- package/dist/styles/themes.css.map +1 -1
- package/package.json +1 -1
package/dist/actions/index.js
CHANGED
|
@@ -7,214 +7,9 @@ import * as logger2 from '@superbuilders/slog';
|
|
|
7
7
|
// src/parser.ts
|
|
8
8
|
|
|
9
9
|
// src/html/sanitize.ts
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
// Text content
|
|
14
|
-
"p",
|
|
15
|
-
"span",
|
|
16
|
-
"div",
|
|
17
|
-
"br",
|
|
18
|
-
"hr",
|
|
19
|
-
// Headings
|
|
20
|
-
"h1",
|
|
21
|
-
"h2",
|
|
22
|
-
"h3",
|
|
23
|
-
"h4",
|
|
24
|
-
"h5",
|
|
25
|
-
"h6",
|
|
26
|
-
// Formatting
|
|
27
|
-
"b",
|
|
28
|
-
"i",
|
|
29
|
-
"u",
|
|
30
|
-
"strong",
|
|
31
|
-
"em",
|
|
32
|
-
"mark",
|
|
33
|
-
"small",
|
|
34
|
-
"sub",
|
|
35
|
-
"sup",
|
|
36
|
-
"code",
|
|
37
|
-
"pre",
|
|
38
|
-
"kbd",
|
|
39
|
-
// Lists
|
|
40
|
-
"ul",
|
|
41
|
-
"ol",
|
|
42
|
-
"li",
|
|
43
|
-
"dl",
|
|
44
|
-
"dt",
|
|
45
|
-
"dd",
|
|
46
|
-
// Tables
|
|
47
|
-
"table",
|
|
48
|
-
"thead",
|
|
49
|
-
"tbody",
|
|
50
|
-
"tfoot",
|
|
51
|
-
"tr",
|
|
52
|
-
"th",
|
|
53
|
-
"td",
|
|
54
|
-
"caption",
|
|
55
|
-
"colgroup",
|
|
56
|
-
"col",
|
|
57
|
-
// Media
|
|
58
|
-
"img",
|
|
59
|
-
"audio",
|
|
60
|
-
"video",
|
|
61
|
-
"source",
|
|
62
|
-
"track",
|
|
63
|
-
// Semantic
|
|
64
|
-
"article",
|
|
65
|
-
"section",
|
|
66
|
-
"nav",
|
|
67
|
-
"aside",
|
|
68
|
-
"header",
|
|
69
|
-
"footer",
|
|
70
|
-
"main",
|
|
71
|
-
"figure",
|
|
72
|
-
"figcaption",
|
|
73
|
-
"blockquote",
|
|
74
|
-
"cite",
|
|
75
|
-
// Interactive
|
|
76
|
-
"details",
|
|
77
|
-
"summary",
|
|
78
|
-
// Links
|
|
79
|
-
"a",
|
|
80
|
-
// Forms (for future interactive elements)
|
|
81
|
-
"label",
|
|
82
|
-
"button"
|
|
83
|
-
]),
|
|
84
|
-
allowedAttributes: {
|
|
85
|
-
// Global attributes
|
|
86
|
-
"*": /* @__PURE__ */ new Set(["class", "id", "lang", "dir", "title", "style"]),
|
|
87
|
-
// Specific attributes
|
|
88
|
-
img: /* @__PURE__ */ new Set(["src", "alt", "width", "height", "style"]),
|
|
89
|
-
a: /* @__PURE__ */ new Set(["href", "target", "rel"]),
|
|
90
|
-
audio: /* @__PURE__ */ new Set(["src", "controls", "loop", "muted"]),
|
|
91
|
-
video: /* @__PURE__ */ new Set(["src", "controls", "loop", "muted", "width", "height", "poster"]),
|
|
92
|
-
source: /* @__PURE__ */ new Set(["src", "type"]),
|
|
93
|
-
track: /* @__PURE__ */ new Set(["src", "kind", "srclang", "label"])
|
|
94
|
-
},
|
|
95
|
-
allowDataAttributes: false,
|
|
96
|
-
allowMathML: true
|
|
97
|
-
};
|
|
98
|
-
var MATHML_TAGS = /* @__PURE__ */ new Set([
|
|
99
|
-
// Root
|
|
100
|
-
"math",
|
|
101
|
-
// Token elements
|
|
102
|
-
"mi",
|
|
103
|
-
"mn",
|
|
104
|
-
"mo",
|
|
105
|
-
"mtext",
|
|
106
|
-
"mspace",
|
|
107
|
-
"ms",
|
|
108
|
-
// Layout
|
|
109
|
-
"mrow",
|
|
110
|
-
"mfrac",
|
|
111
|
-
"msqrt",
|
|
112
|
-
"mroot",
|
|
113
|
-
"mstyle",
|
|
114
|
-
"merror",
|
|
115
|
-
"mpadded",
|
|
116
|
-
"mphantom",
|
|
117
|
-
"mfenced",
|
|
118
|
-
"menclose",
|
|
119
|
-
// Scripts and limits
|
|
120
|
-
"msub",
|
|
121
|
-
"msup",
|
|
122
|
-
"msubsup",
|
|
123
|
-
"munder",
|
|
124
|
-
"mover",
|
|
125
|
-
"munderover",
|
|
126
|
-
"mmultiscripts",
|
|
127
|
-
"mprescripts",
|
|
128
|
-
"none",
|
|
129
|
-
// Tables
|
|
130
|
-
"mtable",
|
|
131
|
-
"mtr",
|
|
132
|
-
"mtd",
|
|
133
|
-
"maligngroup",
|
|
134
|
-
"malignmark",
|
|
135
|
-
// Elementary math
|
|
136
|
-
"mstack",
|
|
137
|
-
"mlongdiv",
|
|
138
|
-
"msgroup",
|
|
139
|
-
"msrow",
|
|
140
|
-
"mscarries",
|
|
141
|
-
"mscarry",
|
|
142
|
-
"msline",
|
|
143
|
-
// Semantic
|
|
144
|
-
"semantics",
|
|
145
|
-
"annotation",
|
|
146
|
-
"annotation-xml"
|
|
147
|
-
]);
|
|
148
|
-
function sanitizeHtml(html, config = {}) {
|
|
149
|
-
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
150
|
-
const dangerousPatterns = [
|
|
151
|
-
/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
|
|
152
|
-
/javascript:/gi,
|
|
153
|
-
/on\w+\s*=/gi,
|
|
154
|
-
// Event handlers
|
|
155
|
-
/<iframe\b/gi,
|
|
156
|
-
/<embed\b/gi,
|
|
157
|
-
/<object\b/gi,
|
|
158
|
-
/data:text\/html/gi
|
|
159
|
-
];
|
|
160
|
-
let sanitized = html;
|
|
161
|
-
for (const pattern of dangerousPatterns) {
|
|
162
|
-
sanitized = sanitized.replace(pattern, "");
|
|
163
|
-
}
|
|
164
|
-
const cleaned = cleanHtml(sanitized, cfg);
|
|
165
|
-
return cleaned;
|
|
166
|
-
}
|
|
167
|
-
function cleanHtml(html, config) {
|
|
168
|
-
let cleaned = html;
|
|
169
|
-
const tagPattern = /<\/?([a-zA-Z][a-zA-Z0-9-]*)([^>]*)>/g;
|
|
170
|
-
cleaned = cleaned.replace(tagPattern, (match, tagName, _attrs) => {
|
|
171
|
-
const tag = tagName.toLowerCase();
|
|
172
|
-
const isMathML = tag === "math" || cleaned.includes("<math");
|
|
173
|
-
if (isMathML && config.allowMathML && MATHML_TAGS.has(tag)) {
|
|
174
|
-
return match;
|
|
175
|
-
}
|
|
176
|
-
if (config.allowedTags.has(tag)) {
|
|
177
|
-
return cleanAttributesString(match, tag, config);
|
|
178
|
-
}
|
|
179
|
-
return "";
|
|
180
|
-
});
|
|
181
|
-
return cleaned;
|
|
182
|
-
}
|
|
183
|
-
function cleanAttributesString(tagString, tagName, config) {
|
|
184
|
-
const attrPattern = /\s+([a-zA-Z][a-zA-Z0-9-:]*)(?:="([^"]*)"|'([^']*)'|=([^\s>]+)|(?=\s|>))/g;
|
|
185
|
-
let cleanedTag = `<${tagString.startsWith("</") ? "/" : ""}${tagName}`;
|
|
186
|
-
let match = attrPattern.exec(tagString);
|
|
187
|
-
while (match !== null) {
|
|
188
|
-
const attrName = (match[1] || "").toLowerCase();
|
|
189
|
-
const attrValue = match[2] || match[3] || match[4] || "";
|
|
190
|
-
const globalAttrs = config.allowedAttributes["*"] || /* @__PURE__ */ new Set();
|
|
191
|
-
const tagAttrs = config.allowedAttributes[tagName] || /* @__PURE__ */ new Set();
|
|
192
|
-
let isAllowed = globalAttrs.has(attrName) || tagAttrs.has(attrName);
|
|
193
|
-
if (tagName === "img" && (attrName === "width" || attrName === "height" || attrName === "style")) {
|
|
194
|
-
isAllowed = false;
|
|
195
|
-
}
|
|
196
|
-
if (!isAllowed && config.allowDataAttributes && attrName.startsWith("data-")) {
|
|
197
|
-
isAllowed = true;
|
|
198
|
-
}
|
|
199
|
-
if (isAllowed && !isDangerousAttributeValue(attrName, attrValue)) {
|
|
200
|
-
cleanedTag += ` ${attrName}="${attrValue.replace(/"/g, """)}"`;
|
|
201
|
-
}
|
|
202
|
-
match = attrPattern.exec(tagString);
|
|
203
|
-
}
|
|
204
|
-
cleanedTag += ">";
|
|
205
|
-
return cleanedTag;
|
|
206
|
-
}
|
|
207
|
-
function isDangerousAttributeValue(name, value) {
|
|
208
|
-
const valueLower = value.toLowerCase().trim();
|
|
209
|
-
if (name === "href" || name === "src") {
|
|
210
|
-
if (valueLower.startsWith("javascript:") || valueLower.startsWith("data:text/html")) {
|
|
211
|
-
return true;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
if (valueLower.includes("javascript:") || valueLower.includes("onerror=")) {
|
|
215
|
-
return true;
|
|
216
|
-
}
|
|
217
|
-
return false;
|
|
10
|
+
function sanitizeHtml(html) {
|
|
11
|
+
if (!html) return html;
|
|
12
|
+
return html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "").replace(/\s+on\w+\s*=\s*["'][^"']*["']/gi, "").replace(/\s+on\w+\s*=\s*[^\s>]+/gi, "").replace(/\b(href|src)\s*=\s*["']?\s*javascript:[^"'>\s]*/gi, "").replace(/\b(href|src)\s*=\s*["']?\s*data:text\/html[^"'>\s]*/gi, "").replace(/<(iframe|embed|object)\b[^>]*>[\s\S]*?<\/\1>/gi, "").replace(/<(iframe|embed|object)\b[^>]*\/?>/gi, "");
|
|
218
13
|
}
|
|
219
14
|
|
|
220
15
|
// src/html/serialize.ts
|
|
@@ -1300,7 +1095,7 @@ function buildDisplayModelFromXml(qtiXml) {
|
|
|
1300
1095
|
return { type: "stimulus", html: sanitizeHtml(block.html) };
|
|
1301
1096
|
}
|
|
1302
1097
|
if (block.type === "richStimulus") {
|
|
1303
|
-
const html = sanitizeHtml(block.html
|
|
1098
|
+
const html = sanitizeHtml(block.html);
|
|
1304
1099
|
const inlineEmbeds = {};
|
|
1305
1100
|
for (const [key, embed] of Object.entries(block.inlineEmbeds)) {
|
|
1306
1101
|
inlineEmbeds[key] = {
|
|
@@ -1399,7 +1194,7 @@ function buildDisplayModelFromXml(qtiXml) {
|
|
|
1399
1194
|
responseId: interaction.responseIdentifier,
|
|
1400
1195
|
gapTexts,
|
|
1401
1196
|
gaps,
|
|
1402
|
-
contentHtml: sanitizeHtml(interaction.contentHtml
|
|
1197
|
+
contentHtml: sanitizeHtml(interaction.contentHtml)
|
|
1403
1198
|
}
|
|
1404
1199
|
};
|
|
1405
1200
|
}
|