/*
проверка уязвимостей в контенте

выводим предупреждения, если в контенте обнаруживаются
- не разрешённые в конфиге html-тэги или атрибуты
- небезопасные значения атрибутов (такие как "javascript:...")

в массиве allowed_html_tags в конфиге:

"span": [ "id", "class" ]
разрешает тэги <span> с любыми из указанных атрибутов или без атрибутов

"span": "*"
разрешает <span> с любыми атрибутами

"span": null
разрешает <span> только без атрибутов
*/

const { Parser } = require('htmlparser2');
const cli = require('../cli-util');
const config = require('./config');

const allowedTags = config.allowed_html_tags || {};

module.exports = function audit(content, key) {
    if (config.content_audit === false) {
        return;
    }

    if (typeof key === 'number') {
        // чтобы любые обычные ключи, включая 0, были truthy
        key = String(key);
    }

    if (Array.isArray(content)) {
        content.forEach((item, k) => {
            audit(content[k], `${key || ''}[${k}]`);
        });
        return;
    }

    if (content && typeof content === 'object') {
        Object.keys(content).forEach(k => {
            audit(content[k], key ? `${key}.${k}` : k);
        });
        return;
    }

    const contentKey = key ? cli.bold(key) : 'content';

    if (typeof content !== 'string') {
        console.log(`! ${contentKey} is ${cli.warning('non-string')}`);
        return;
    }

    const unallowedTags = [];
    const unsafeTags = [];

    const parser = new Parser({
        onopentag(name, attrs) {
            if (!(name in allowedTags)) {
                unallowedTags.push(`<${name}>`);
                return;
            }

            const allowedAttrs = allowedTags[name];

            if (allowedAttrs === '*') {
                return;
            }

            Object.entries(attrs).forEach(([attrName, attrValue]) => {
                const attrTag = `<${name} ${attrName}>`;

                if (!Array.isArray(allowedAttrs) || !allowedAttrs.includes(attrName)) {
                    unallowedTags.push(attrTag);
                }

                if (/^\s*javascript:/i.test(attrValue)) {
                    unsafeTags.push(attrTag);
                }
            });
        }
    }, { decodeEntities: true });

    parser.write(content);
    parser.end();

    if (unallowedTags.length) {
        console.log(`! ${contentKey} contains ${cli.warning('unallowed')} ${toTagList(unallowedTags)}`);
    }

    if (unsafeTags.length) {
        console.log(`! ${contentKey} contains ${cli.error('unsafe')} ${toTagList(unsafeTags)}`);
    }
};

function toTagList(tags) {
    // только уникальные
    return [...new Set(tags)].map(cli.bold).join(', ');
}
