- commit
- 434471f76b74a99fb75dc8d1bab409c5f11cdb48
- parent
- 1ef3a8d2e8f9b96feb82b43aba3ef374121ddc13
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2019-03-25 19:44
Merge branch 'feature-fuzz'
Diffstat
| M | Makefile | 11 | +++++++++++ |
| A | fuzz.html | 38 | ++++++++++++++++++++++++++++++++++++++ |
| A | fuzz/fuzzer.js | 48 | ++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | fuzz/html.js | 147 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | fuzz/index.js | 66 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | package.json | 1 | + |
6 files changed, 311 insertions, 0 deletions
diff --git a/Makefile b/Makefile
@@ -1,3 +1,14 @@ -1 1 all: babel.js fuzz.js -1 2 1 3 babel.js: src/babel.js src/axs.js src/eval.patch 2 4 browserify $< -o $@ 3 5 patch $@ src/eval.patch -1 6 -1 7 fuzz.js: fuzz/index.js fuzz/*.js node_modules/aria-api/instrumented.js node_modules/aria-api/lib/name-inst.js -1 8 browserify $< -o $@ -1 9 -1 10 node_modules/aria-api/instrumented.js: node_modules/aria-api/index.js -1 11 cat $< | sed 's/name\.js/name-inst\.js/' > $@ -1 12 -1 13 node_modules/aria-api/lib/name-inst.js: node_modules/aria-api/lib/name.js -1 14 npx nyc instrument $< > $@
diff --git a/fuzz.html b/fuzz.html
@@ -0,0 +1,38 @@ -1 1 <!DOCTYPE html> -1 2 <html> -1 3 <head> -1 4 <meta charset="utf-8" /> -1 5 <title>Babelacc fuzzer</title> -1 6 <link rel="stylesheet" href="style.css" /> -1 7 </head> -1 8 <body> -1 9 <table> -1 10 <thead> -1 11 <tr> -1 12 <th>tested paths</th> -1 13 <th>errors found</th> -1 14 </tr> -1 15 </thead> -1 16 <tbody> -1 17 <tr> -1 18 <td id="ba-fingerprints">0</td> -1 19 <td id="ba-errors">0</td> -1 20 </tr> -1 21 </tbody> -1 22 </table> -1 23 -1 24 <table> -1 25 <thead> -1 26 <tr> -1 27 <th>HTML</th> -1 28 <th>accdc</th> -1 29 <th>aria-api</th> -1 30 </tr> -1 31 </thead> -1 32 <tbody id="ba-reports"></tbody> -1 33 </table> -1 34 <div id="ba-preview"></div> -1 35 -1 36 <script src="fuzz.js"></script> -1 37 </body> -1 38 </html>
diff --git a/fuzz/fuzzer.js b/fuzz/fuzzer.js
@@ -0,0 +1,48 @@
-1 1 var getFingerprint = function(covPath) {
-1 2 var coverage = window.__coverage__[covPath].b;
-1 3 var fingerprint = Object.values(coverage).map(x => x.join(':')).join('-');
-1 4 for (var key in coverage) {
-1 5 for (var i = 0; i < coverage[key].length; i++) {
-1 6 coverage[key][i] = 0;
-1 7 }
-1 8 }
-1 9 return fingerprint;
-1 10 };
-1 11
-1 12 var run = function(corpus, oracle, covPath, onFingerprint, onReport, done) {
-1 13 var fingerprints = [];
-1 14 var queue = [];
-1 15 var count = 0;
-1 16
-1 17 corpus.forEach(function(item) {
-1 18 queue.push(item);
-1 19 });
-1 20
-1 21 var step = function() {
-1 22 if (queue.length) {
-1 23 var item = queue.shift();
-1 24 var report = oracle(item);
-1 25 var fingerprint = getFingerprint(covPath);
-1 26
-1 27 if (!fingerprints.includes(fingerprint)) {
-1 28 fingerprints.push(fingerprint);
-1 29 item.mutate().forEach(mutation => queue.push(mutation));
-1 30 onFingerprint(fingerprint, fingerprints.length);
-1 31
-1 32 if (report) {
-1 33 onReport(report);
-1 34 }
-1 35 }
-1 36
-1 37 setTimeout(step);
-1 38 } else {
-1 39 done();
-1 40 }
-1 41 };
-1 42
-1 43 step();
-1 44 };
-1 45
-1 46 module.exports = {
-1 47 'run': run,
-1 48 };
diff --git a/fuzz/html.js b/fuzz/html.js
@@ -0,0 +1,147 @@
-1 1 var constants = require('aria-api/lib/constants');
-1 2
-1 3 var attributes = [
-1 4 ['role', Array.prototype.concat.apply([], Object.values(constants.subRoles)).filter((v, i, a) => a.indexOf(v) === i)],
-1 5 ['hidden', ['']],
-1 6 ['aria-hidden', ['', 'true', 'false']],
-1 7 ['aria-label', ['', '__random__']],
-1 8 ['title', ['', '__random__']],
-1 9 ['value', ['', '__random__']],
-1 10 ['placeholder', ['', '__random__']],
-1 11 ['alt', ['', '__random__']],
-1 12 ['aria-valuenow', ['', '__random__']],
-1 13 ['aria-valuetext', ['', '__random__']],
-1 14 ['aria-labelledby', ['__randint__']],
-1 15 ['aria-owns', ['__randint__']],
-1 16 ['list', ['__randint__']],
-1 17 ['for', ['__randint__']],
-1 18 ['type', ['', '__random__', 'hidden', 'checkbox', 'text', 'password', 'color', 'reset']],
-1 19 ['style', ['', '__random__', 'display: none', 'display: block', 'display: inline-block', 'display: inline', 'visibility: hidden']],
-1 20 ];
-1 21
-1 22 var tags = ['a', 'button', 'form', 'label', 'input', 'article', 'table', 'td', 'tr', 'th', 'pre', 'legend', 'h1', 'div', 'span', 'fieldset', 'img', 'abbr', 'strong', 'br', 'hr', 'select', 'option', 'datalist'];
-1 23
-1 24 var randomInt = function(n) {
-1 25 return Math.floor(Math.random() * n);
-1 26 };
-1 27
-1 28 var randomChoice = function(list) {
-1 29 return list[randomInt(list.length)];
-1 30 };
-1 31
-1 32 var randomString = function(len) {
-1 33 var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 -_.,#';
-1 34 var result = '';
-1 35 for (var i = 0; i < len; i++) {
-1 36 result += randomChoice(chars);
-1 37 }
-1 38 return result;
-1 39 };
-1 40
-1 41 function AttributeList(len) {
-1 42 this.value = [];
-1 43 for (var i = 0; i < len; i++) {
-1 44 var attr = randomChoice(attributes);
-1 45 var value = randomChoice(attr[1]);
-1 46 if (value === '__random__') {
-1 47 // shortcut: always use fixed string length
-1 48 value = randomString(10);
-1 49 } else if (value === '__randint__') {
-1 50 value = randomInt(len);
-1 51 }
-1 52 this.value.push(attr[0] + '="' + value + '"');
-1 53 }
-1 54 }
-1 55
-1 56 AttributeList.prototype.shrink = function() {
-1 57 var result = [];
-1 58 for (var i = 0; i < this.value.length; i++) {
-1 59 var item = new AttributeList(0);
-1 60 for (var j = 0; j < this.value.length; j++) {
-1 61 if (j !== i) {
-1 62 item.value.push(this.value[j]);
-1 63 }
-1 64 }
-1 65 result.push(item);
-1 66 }
-1 67 return result;
-1 68 };
-1 69
-1 70 function Element(len, ctx) {
-1 71 ctx = ctx || {k: 0};
-1 72 this.tag = randomChoice(tags);
-1 73 this.id = ctx.k;
-1 74 this.content = ctx.k;
-1 75 ctx.k += 1;
-1 76 this.attrs = new AttributeList(randomInt(len));
-1 77 this.children = new Children(randomInt(len), ctx);
-1 78 }
-1 79
-1 80 Element.prototype.shrink = function() {
-1 81 var result = [];
-1 82 var tag = this.tag;
-1 83 var id = this.id;
-1 84 var content = this.content;
-1 85 var attrsList = [this.attrs].concat(this.attrs.shrink());
-1 86 var childrenList = [this.children].concat(this.children.shrink());
-1 87 attrsList.forEach(function(attrs) {
-1 88 childrenList.forEach(function(children) {
-1 89 if (attrs !== this.attrs || children !== this.children) {
-1 90 var item = new Element(0, {});
-1 91 item.tag = tag;
-1 92 item.id = id;
-1 93 item.content = content;
-1 94 item.attrs = attrs;
-1 95 item.children = children;
-1 96 result.push(item);
-1 97 }
-1 98 });
-1 99 });
-1 100 return result;
-1 101 };
-1 102
-1 103 Element.prototype.mutate = function() {
-1 104 return this.shrink();
-1 105 };
-1 106
-1 107 Element.prototype.toString = function() {
-1 108 var s = '<' + this.tag + ' id="' + this.id + '"';
-1 109 this.attrs.value.forEach(function(attr) {
-1 110 s += ' ' + attr;
-1 111 });
-1 112 s += '>' + this.content;
-1 113 this.children.value.forEach(function(child) {
-1 114 s += child.toString();
-1 115 });
-1 116 s += '</' + this.tag + '>';
-1 117 return s;
-1 118 };
-1 119
-1 120 function Children(len, ctx) {
-1 121 this.value = [];
-1 122 for (var i = 0; i < len; i++) {
-1 123 this.value.push(new Element(randomInt(len), ctx));
-1 124 }
-1 125 }
-1 126
-1 127 Children.prototype.shrink = function() {
-1 128 var result = [];
-1 129 for (var i = 0; i < this.value.length; i++) {
-1 130 var item = new Children(0, {});
-1 131 for (var j = 0; j < this.value.length; j++) {
-1 132 if (j !== i) {
-1 133 // shortcut: pick random shrink result
-1 134 var shrunken = this.value[j].shrink();
-1 135 if (shrunken.length) {
-1 136 item.value.push(randomChoice(shrunken));
-1 137 }
-1 138 }
-1 139 }
-1 140 result.push(item);
-1 141 }
-1 142 return result;
-1 143 };
-1 144
-1 145 module.exports = {
-1 146 'Element': Element,
-1 147 };
diff --git a/fuzz/index.js b/fuzz/index.js
@@ -0,0 +1,66 @@
-1 1 var ariaApi = require('aria-api/instrumented.js');
-1 2 var accdc = require('w3c-alternative-text-computation');
-1 3
-1 4 var fuzzer = require('./fuzzer');
-1 5 var html = require('./html');
-1 6
-1 7 var preview = document.querySelector('#ba-preview');
-1 8 var fingerprints = document.querySelector('#ba-fingerprints');
-1 9 var errors = document.querySelector('#ba-errors');
-1 10 var reports = document.querySelector('#ba-reports');
-1 11
-1 12 var oracle = function(input) {
-1 13 preview.innerHTML = input.toString();
-1 14 var el = preview.querySelector('#test') || preview.children[0] || preview;
-1 15 var v1, v2;
-1 16
-1 17 try {
-1 18 v1 = accdc.calcNames(el).name;
-1 19 } catch (error) {
-1 20 v1 = error;
-1 21 }
-1 22
-1 23 try {
-1 24 v2 = ariaApi.getName(el);
-1 25 } catch (error) {
-1 26 v2 = error;
-1 27 }
-1 28
-1 29 if (v1 !== v2) {
-1 30 return {
-1 31 'html': preview.innerHTML,
-1 32 'v1': v1,
-1 33 'v2': v2,
-1 34 };
-1 35 }
-1 36 };
-1 37
-1 38 var renderReport = function(report) {
-1 39 var tr = document.createElement('tr');
-1 40 var td1 = document.createElement('td');
-1 41 td1.textContent = report.html;
-1 42 tr.append(td1);
-1 43 var td2 = document.createElement('td');
-1 44 td2.textContent = report.v1;
-1 45 tr.append(td2);
-1 46 var td3 = document.createElement('td');
-1 47 td3.textContent = report.v2;
-1 48 tr.append(td3);
-1 49 return tr;
-1 50 };
-1 51
-1 52 document.addEventListener('DOMContentLoaded', function() {
-1 53 var covPath = 'node_modules/aria-api/lib/name.js';
-1 54 var corpus = [];
-1 55 for (var i = 0; i < 10; i++) {
-1 56 corpus.push(new html.Element(10));
-1 57 }
-1 58 fuzzer.run(corpus, oracle, covPath, function(fingerprint, c) {
-1 59 fingerprints.textContent = c;
-1 60 }, function(report) {
-1 61 reports.append(renderReport(report));
-1 62 errors.textContent = reports.children.length;
-1 63 }, function() {
-1 64 preview.innerHTML = 'DONE';
-1 65 });
-1 66 });
diff --git a/package.json b/package.json
@@ -6,6 +6,7 @@ 6 6 "accessibility-developer-tools": "^2.12.0", 7 7 "aria-api": "^0.2.5", 8 8 "axe-core": "^3.1.2", -1 9 "nyc": "^13.3.0", 9 10 "w3c-alternative-text-computation": "github:accdc/w3c-alternative-text-computation" 10 11 }, 11 12 "repository": {