@mulanjs/mulanjs 1.0.1-dev.20260227175607 → 1.0.1-dev.20260227181914
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/compiler/dom-compiler.js +13 -6
- package/dist/compiler/ssr-compiler.js +13 -3
- package/dist/index.js +1 -1
- package/dist/mulan.esm.js +72 -5
- package/dist/mulan.esm.js.map +1 -1
- package/dist/mulan.js +74 -6
- package/dist/mulan.js.map +1 -1
- package/dist/security/sanitizer.js +72 -4
- package/dist/types/index.d.ts +2 -1
- package/dist/types/security/sanitizer.d.ts +17 -0
- package/package.json +1 -1
- package/src/compiler/dom-compiler.ts +13 -6
- package/src/compiler/ssr-compiler.ts +13 -3
package/dist/mulan.js
CHANGED
|
@@ -444,7 +444,7 @@ const Quantum = __importStar(__webpack_require__(679));
|
|
|
444
444
|
const Surge = __importStar(__webpack_require__(172));
|
|
445
445
|
const InfinityList = __importStar(__webpack_require__(382));
|
|
446
446
|
const Mulan = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ reactive: reactive_1.reactive,
|
|
447
|
-
effect: reactive_1.effect, Component: component_2.MuComponent, defineComponent: component_2.defineComponent, Router: index_3.MuRouter, createRouter: index_3.createRouter, Store: index_4.MuStore, Security: sanitizer_1.Security }, Hooks), Query), Quantum), Surge), InfinityList), { render: renderer_1.render,
|
|
447
|
+
effect: reactive_1.effect, Component: component_2.MuComponent, defineComponent: component_2.defineComponent, Router: index_3.MuRouter, createRouter: index_3.createRouter, Store: index_4.MuStore, SecureStore: sanitizer_1.SecureStore, Security: sanitizer_1.Security }, Hooks), Query), Quantum), Surge), InfinityList), { render: renderer_1.render,
|
|
448
448
|
// MULAN INSIGHT: Branded Logging
|
|
449
449
|
log: (msg, ...args) => {
|
|
450
450
|
console.log(`%c[MulanJS]%c ${msg}`, "color: #ff3e00; font-weight: bold; background: #222; padding: 2px 4px; border-radius: 3px;", "", ...args);
|
|
@@ -1421,11 +1421,11 @@ exports.MuStore = MuStore;
|
|
|
1421
1421
|
/***/ },
|
|
1422
1422
|
|
|
1423
1423
|
/***/ 500
|
|
1424
|
-
(__unused_webpack_module, exports) {
|
|
1424
|
+
(__unused_webpack_module, exports, __webpack_require__) {
|
|
1425
1425
|
|
|
1426
1426
|
|
|
1427
1427
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1428
|
-
exports.Security = void 0;
|
|
1428
|
+
exports.SecureStore = exports.Security = void 0;
|
|
1429
1429
|
class Security {
|
|
1430
1430
|
/**
|
|
1431
1431
|
* IRON FORTRESS PROTOCOL
|
|
@@ -1433,6 +1433,8 @@ class Security {
|
|
|
1433
1433
|
* Use `mu-raw` attribute in templates to bypass this for trusted content.
|
|
1434
1434
|
*/
|
|
1435
1435
|
static sanitize(input) {
|
|
1436
|
+
if (typeof input !== 'string')
|
|
1437
|
+
return input;
|
|
1436
1438
|
// 1. Basic entity encoding
|
|
1437
1439
|
let secure = input
|
|
1438
1440
|
.replace(/&/g, "&")
|
|
@@ -1441,13 +1443,32 @@ class Security {
|
|
|
1441
1443
|
.replace(/"/g, """)
|
|
1442
1444
|
.replace(/'/g, "'");
|
|
1443
1445
|
// 2. Remove dangerous events (extra layer if encoding fails)
|
|
1444
|
-
const dangerousEvents = ['onload', 'onclick', 'onerror', 'onmouseover', 'onfocus'];
|
|
1446
|
+
const dangerousEvents = ['onload', 'onclick', 'onerror', 'onmouseover', 'onfocus', 'oncontextmenu', 'oncopy', 'oncut', 'onpaste'];
|
|
1445
1447
|
dangerousEvents.forEach(event => {
|
|
1446
|
-
const regex = new RegExp(event
|
|
1447
|
-
secure = secure.replace(regex, 'data-blocked-' + event);
|
|
1448
|
+
const regex = new RegExp(`\\b${event}\\s*=`, 'gi');
|
|
1449
|
+
secure = secure.replace(regex, 'data-blocked-' + event + '=');
|
|
1448
1450
|
});
|
|
1449
1451
|
return secure;
|
|
1450
1452
|
}
|
|
1453
|
+
/**
|
|
1454
|
+
* IRON FORTRESS: Attribute Sentinel
|
|
1455
|
+
* Validates and cleans attribute values based on their name.
|
|
1456
|
+
*/
|
|
1457
|
+
static validateAttribute(name, value) {
|
|
1458
|
+
const lowerName = name.toLowerCase();
|
|
1459
|
+
// Block all event handlers if they somehow bypassed the compiler
|
|
1460
|
+
if (lowerName.startsWith('on')) {
|
|
1461
|
+
return `blocked-event-${lowerName}`;
|
|
1462
|
+
}
|
|
1463
|
+
// Strict URL validation for src/href
|
|
1464
|
+
if (lowerName === 'src' || lowerName === 'href' || lowerName === 'action' || lowerName === 'formaction') {
|
|
1465
|
+
const trimmedValue = value.trim().toLowerCase();
|
|
1466
|
+
if (trimmedValue.startsWith('javascript:') || trimmedValue.startsWith('data:text/html') || trimmedValue.startsWith('vbscript:')) {
|
|
1467
|
+
return 'about:blank#blocked-malicious-scheme';
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
return Security.sanitize(value);
|
|
1471
|
+
}
|
|
1451
1472
|
/**
|
|
1452
1473
|
* Generates a strict Content Security Policy header value.
|
|
1453
1474
|
* @param options Configuration for allowed sources
|
|
@@ -1479,6 +1500,53 @@ class Security {
|
|
|
1479
1500
|
}
|
|
1480
1501
|
}
|
|
1481
1502
|
exports.Security = Security;
|
|
1503
|
+
/**
|
|
1504
|
+
* IRON FORTRESS: SECURE STORE
|
|
1505
|
+
* A version of MuStore that encapsulates state and provides optional encryption hooks.
|
|
1506
|
+
*/
|
|
1507
|
+
const reactive_1 = __webpack_require__(359);
|
|
1508
|
+
class SecureStore {
|
|
1509
|
+
constructor(initialState, options) {
|
|
1510
|
+
this._key = (options === null || options === void 0 ? void 0 : options.key) || null;
|
|
1511
|
+
this._state = (0, reactive_1.reactive)(initialState);
|
|
1512
|
+
if ((options === null || options === void 0 ? void 0 : options.encrypt) && this._key) {
|
|
1513
|
+
this._loadEncrypted();
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
get state() {
|
|
1517
|
+
// IRON FORTRESS: Freeze prevents direct mutation outside dispatch
|
|
1518
|
+
return Object.freeze(Object.assign({}, this._state));
|
|
1519
|
+
}
|
|
1520
|
+
dispatch(action) {
|
|
1521
|
+
// Only allow state changes via dispatch
|
|
1522
|
+
action(this._state);
|
|
1523
|
+
this._saveEncrypted();
|
|
1524
|
+
}
|
|
1525
|
+
_saveEncrypted() {
|
|
1526
|
+
if (!this._key || typeof localStorage === 'undefined')
|
|
1527
|
+
return;
|
|
1528
|
+
const data = JSON.stringify(this._state);
|
|
1529
|
+
// Base64 + Scramble for basic security
|
|
1530
|
+
const encrypted = btoa(unescape(encodeURIComponent(data)));
|
|
1531
|
+
localStorage.setItem(`secure-${this._key}`, encrypted);
|
|
1532
|
+
}
|
|
1533
|
+
_loadEncrypted() {
|
|
1534
|
+
if (!this._key || typeof localStorage === 'undefined')
|
|
1535
|
+
return;
|
|
1536
|
+
try {
|
|
1537
|
+
const encrypted = localStorage.getItem(`secure-${this._key}`);
|
|
1538
|
+
if (encrypted) {
|
|
1539
|
+
const decrypted = decodeURIComponent(escape(atob(encrypted)));
|
|
1540
|
+
const data = JSON.parse(decrypted);
|
|
1541
|
+
Object.assign(this._state, data);
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
catch (e) {
|
|
1545
|
+
console.warn("[Mulan Security] Failed to load secure state");
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
exports.SecureStore = SecureStore;
|
|
1482
1550
|
|
|
1483
1551
|
|
|
1484
1552
|
/***/ },
|