ec-sri-invoice-signer
Version:
Ecuador SRI invoice signer.
96 lines (95 loc) • 4.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateXmlFeatures = exports.validateDocumentType = exports.parseXml = exports.buildXml = void 0;
const fast_xml_parser_1 = require("fast-xml-parser");
const errors_1 = require("./errors");
const utils_1 = require("../canonicalization/utils");
const constants_1 = require("./constants");
const parseXml = (xml) => {
const parserOptions = {
commentPropName: '#comment', // need to include comments to keep the possible whitespace that is left when removing them. Otherwhise the library gets rid of it
ignoreAttributes: false,
ignoreDeclaration: true,
parseTagValue: false,
preserveOrder: true,
trimValues: false,
processEntities: false,
ignorePiTags: true,
attributeValueProcessor: (name, value) => {
return (0, utils_1.processAttributeValue)(value);
},
tagValueProcessor: (name, value) => {
return (0, utils_1.processTagValue)(value);
}
};
try {
const parser = new fast_xml_parser_1.XMLParser(parserOptions);
return parser.parse(xml);
}
catch (err) {
throw new errors_1.XmlFormatError();
}
};
exports.parseXml = parseXml;
const buildXml = (data) => {
const builderOptions = {
ignoreAttributes: false,
preserveOrder: true,
processEntities: false,
suppressEmptyNode: false
};
const builder = new fast_xml_parser_1.XMLBuilder(builderOptions);
return builder.build(data);
};
exports.buildXml = buildXml;
const validateDocumentType = (xml) => {
const parsed = parseXml(xml);
if (!parsed || parsed.length === 0) {
throw new errors_1.XmlFormatError();
}
// Find the root element
const rootElement = Object.keys(parsed[0]).find(key => key !== ':@' && key !== '#text' && !key.startsWith('#'));
if (!rootElement) {
throw new errors_1.XmlFormatError();
}
if (!constants_1.SupportedDocumentTypes.has(rootElement)) {
throw new errors_1.UnsupportedDocumentTypeError(rootElement);
}
};
exports.validateDocumentType = validateDocumentType;
const validateXmlFeatures = (xml) => {
// Check for DOCTYPE declarations
if (xml.includes('<!DOCTYPE')) {
throw new errors_1.UnsupportedXmlFeatureError('DOCTYPE declarations', 'DOCTYPE declarations are not supported. Remove any <!DOCTYPE> declarations from your XML.');
}
// Check for xml-prefixed attributes
const xmlAttributePattern = /\w+:\w+\s*=/g;
const xmlAttributeMatches = xml.match(xmlAttributePattern);
if (xmlAttributeMatches) {
for (const match of xmlAttributeMatches) {
if (match.startsWith('xml:')) {
throw new errors_1.UnsupportedXmlFeatureError('xml-prefixed attributes', `Found attribute "${match}". Remove any xml: prefixed attributes from your XML.`);
}
}
}
// Check for namespace declarations (xmlns)
if (xml.includes('xmlns:')) {
throw new errors_1.UnsupportedXmlFeatureError('namespace declarations', 'Namespace declarations (xmlns:) are not supported in the document root. This library adds the necessary namespaces automatically during signing.');
}
// Check for default xmlns declarations
const defaultXmlnsPattern = /<\w+[^>]*\s+xmlns\s*=/;
if (defaultXmlnsPattern.test(xml)) {
throw new errors_1.UnsupportedXmlFeatureError('default namespace declarations', 'Default xmlns declarations are not supported. This library adds the necessary namespaces automatically during signing.');
}
// Check for processing instructions other than XML declaration
const processingInstructionPattern = /<\?[^?]*\?>/g;
const matches = xml.match(processingInstructionPattern);
if (matches) {
for (const match of matches) {
if (!match.startsWith('<?xml')) {
throw new errors_1.UnsupportedXmlFeatureError('processing instructions', `Found processing instruction "${match}". Only XML declarations (<?xml ... ?>) are supported.`);
}
}
}
};
exports.validateXmlFeatures = validateXmlFeatures;