diff --git a/src/nodes/html.ts b/src/nodes/html.ts
index f6f4ea7..cda2835 100644
--- a/src/nodes/html.ts
+++ b/src/nodes/html.ts
@@ -705,10 +705,12 @@ export default class HTMLElement extends Node {
}
const attrs = {} as RawAttributes;
if (this.rawAttrs) {
- const re = /([a-zA-Z()[\]#@$.?:][a-zA-Z0-9-._:()[\]#]*)(?:\s*=\s*((?:'[^']*')|(?:"[^"]*")|\S+))?/g;
+ const re = /([_a-zA-Z()[\]#@$.?:][a-zA-Z0-9-._:()[\]#]*)(?:\s*=\s*((?:'[^']*')|(?:"[^"]*")|\S+))?/g;
let match: RegExpExecArray;
while ((match = re.exec(this.rawAttrs))) {
const key = match[1];
+ // Reject `__proto__` to prevent prototype pollution (issue #129)
+ if (key === '__proto__') continue;
let val = match[2] || null;
if (val && (val[0] === `'` || val[0] === `"`)) val = val.slice(1, val.length - 1);
attrs[key] = attrs[key] || val;
diff --git a/test/tests/issues/129.js b/test/tests/issues/129.js
index 2be789c..2f63d0b 100755
--- a/test/tests/issues/129.js
+++ b/test/tests/issues/129.js
@@ -5,6 +5,7 @@ describe('Prototype pollution', () => {
it('prevents prototype pollution', () => {
const root = parse('');
should(root.firstChild.attributes.polluted).not.be.ok();
- should(root.firstChild.attributes.hasOwnProperty('proto__')).be.ok();
+ should(Object.prototype.polluted).not.be.ok();
+ should(root.firstChild.attributes.hasOwnProperty('__proto__')).not.be.ok();
});
});
diff --git a/test/tests/issues/206.js b/test/tests/issues/206.js
new file mode 100644
index 0000000..3f5a9b7
--- /dev/null
+++ b/test/tests/issues/206.js
@@ -0,0 +1,18 @@
+const { parse } = require('@test/test-target');
+
+// see: https://github.com/taoqf/node-html-parser/issues/206
+describe('attributes starting with an underscore', function () {
+ it('keeps the leading underscore in the attribute name', function () {
+ const root = parse('t
');
+ const div = root.firstChild;
+ div.getAttribute('_foo').should.eql('bar');
+ div.getAttribute('_ngcontent-c1').should.eql('x');
+ div.toString().should.eql('t
');
+ });
+ it('supports the hyperscript "_" attribute', function () {
+ const root = parse('');
+ const button = root.firstChild;
+ button.getAttribute('_').should.eql('on click toggle .red');
+ button.toString().should.eql('');
+ });
+});