import { expect, tap } from '@git.zone/tstest/tapbundle'; import * as einvoice from '../../../ts/index.js'; import * as plugins from '../../plugins.js'; tap.test('PARSE-09: Entity Reference Resolution - Handle XML entities correctly', async () => { console.log('\n=== Testing Entity Reference Resolution ===\n'); // Test predefined XML entities console.log('Testing predefined XML entities:'); const predefinedEntities = [ { name: 'Ampersand', entity: '&', character: '&' }, { name: 'Less than', entity: '<', character: '<' }, { name: 'Greater than', entity: '>', character: '>' }, { name: 'Quote', entity: '"', character: '"' }, { name: 'Apostrophe', entity: ''', character: "'" } ]; for (const entity of predefinedEntities) { const testXml = ` Test ${entity.entity} Company Text with ${entity.entity} entity `; console.log(`\n${entity.name} entity (${entity.entity} = "${entity.character}")`); try { const invoice = new einvoice.EInvoice(); if (invoice.fromXmlString) { await invoice.fromXmlString(testXml); console.log(' ✓ Entity parsed successfully'); } else { console.log(' ⚠️ fromXmlString not available'); } } catch (error) { console.log(` ✗ Error: ${error.message}`); } } // Test numeric character references console.log('\n\nTesting numeric character references:'); const numericRefs = [ { ref: 'A', char: 'A', description: 'Latin capital A' }, { ref: '€', char: '€', description: 'Euro sign' }, { ref: '©', char: '©', description: 'Copyright' }, { ref: 'A', char: 'A', description: 'Latin A (hex)' }, { ref: '€', char: '€', description: 'Euro (hex)' } ]; for (const test of numericRefs) { const xml = ` 100.00 ${test.ref} 2024 `; console.log(`\n${test.ref} = "${test.char}" (${test.description})`); try { const invoice = new einvoice.EInvoice(); if (invoice.fromXmlString) { await invoice.fromXmlString(xml); console.log(' ✓ Numeric reference parsed'); } } catch (error) { console.log(` ✗ Error: ${error.message}`); } } // Test entity security console.log('\n\nTesting entity security:'); const securityTests = [ { name: 'External entity (XXE)', xml: ` ]> &xxe; ` }, { name: 'Entity expansion', xml: ` ]> &lol2; ` } ]; for (const test of securityTests) { console.log(`\n${test.name}:`); try { const invoice = new einvoice.EInvoice(); if (invoice.fromXmlString) { await invoice.fromXmlString(test.xml); console.log(' ⚠️ WARNING: Parser allowed potentially dangerous entities'); } } catch (error) { console.log(' ✓ Parser correctly rejected dangerous entities'); console.log(` Error: ${error.message}`); } } // Test entity usage in real e-invoice patterns console.log('\n\nTesting common e-invoice entity patterns:'); const einvoicePatterns = [ { name: 'Company with ampersand', xml: ` Smith & Jones Ltd. AT&T Communications ` }, { name: 'Currency symbols', xml: ` Price: €100.00 Alternative: £85.00 ` }, { name: 'Legal symbols', xml: ` Product® ` } ]; for (const pattern of einvoicePatterns) { console.log(`\n${pattern.name}:`); try { const invoice = new einvoice.EInvoice(); if (invoice.fromXmlString) { await invoice.fromXmlString(pattern.xml); console.log(' ✓ Pattern parsed successfully'); } } catch (error) { console.log(` ✗ Error: ${error.message}`); } } // Test entity resolution performance console.log('\n\nTesting entity resolution performance:'); const sizes = [10, 50, 100]; for (const size of sizes) { let xml = '\n\n'; for (let i = 0; i < size; i++) { xml += ` Text & more € symbols ©\n`; } xml += ''; const startTime = performance.now(); try { const invoice = new einvoice.EInvoice(); if (invoice.fromXmlString) { await invoice.fromXmlString(xml); const elapsed = performance.now() - startTime; console.log(` ${size * 3} entities: ${elapsed.toFixed(2)}ms`); } } catch (error) { console.log(` Error with ${size} fields: ${error.message}`); } } // Summary console.log('\n\nEntity Reference Resolution Summary:'); console.log('- Predefined XML entities should be supported'); console.log('- Numeric character references are common in e-invoices'); console.log('- Security: External entities should be disabled'); console.log('- Performance: Entity resolution adds minimal overhead'); console.log('- Common patterns: Company names, currency symbols, legal marks'); }); tap.start();