T09 · Insecure Skill Coding Practices
- Location
scripts/forward.js:10- Finding
Unescaped User Input Enables VCard Field and Record Injection
- Content
View full analysis
Vulnerability Details
File Location:
scripts/forward.js, lines 10–19 and 80–94
Vulnerability Type: VCard injection caused by insufficient input validation and output encoding
Risk Level: MediumVulnerable Code
Lines 10–19:
javascript function generateVCard(name, phone) { const cleanPhone = phone.replace(/\D/g, ''); const vcard = `BEGIN:VCARD VERSION:3.0 N:;${name};;; FN:${name} TEL;type=CELL;type=VOICE;waid=${cleanPhone}:+${cleanPhone} END:VCARD`;Lines 80–94:
javascript function multiVCard(contactsJson) { try { const contacts = JSON.parse(contactsJson); const vcards = contacts.map(c => ({ displayName: c.name, vcard: `BEGIN:VCARD VERSION:3.0 N:;${c.name};;; FN:${c.name} TEL;type=CELL;type=VOICE;waid=${c.phone}:+${c.phone} END:VCARD` })); console.log(JSON.stringify({ total: vcards.length, vcards }, null, 2));Technical Analysis
Values controlled through command-line arguments or the
multi-vcardJSON document are interpolated directly into structured VCard content. The contact name is not escaped in either generation path. The multiple-contact path also inserts the phone value without applying the digit-only normalization used bygenerateVCard.VCard text values require syntax-aware escaping. In particular, untrusted carriage returns or line feeds can introduce new properties because each line represents a VCard field. An attacker can also inject
END:VCARDfollowed byBEGIN:VCARDto append an unauthorized contact record. Unescaped backslashes, semicolons, and commas can alter parsing of structured values.JSON serialization does not remediate this issue. Although the outer JSON output escapes line breaks for transport, a consumer that parses the JSON obtains the original malicious line breaks inside the
vcardstring.Attack Path
- An attacker influences a contact name or phone value passed to ...[truncated 1558 chars]
- Remediation
View remediation
Remediation Suggestions
-
Implement a dedicated VCard text-escaping function and apply it to every text field before interpolation:
javascript function escapeVCardText(value) { if (typeof value !== 'string') { throw new TypeError('VCard text values must be strings'); } return value .replace(/\\/g, '\\\\') .replace(/\r\n|\r|\n/g, '\\n') .replace(/;/g, '\\;') .replace(/,/g, '\\,'); } -
Validate and normalize phone numbers consistently in both generation paths:
javascript function normalizePhone(value) { if (typeof value !== 'string') { throw new TypeError('Phone number must be a string'); } const normalized = value.replace(/\D/g, ''); if (!/^\d{7,15}$/.test(normalized)) { throw new Error('Invalid phone number'); } return normalized; } -
Validate that
multi-vcardinput is an array and that every entry is a non-null object containing stringnameandphoneproperties. Reject malformed records rather than coercing arbitrary values. -
Prefer a maintained VCard serialization library that correctly implements the selected VCard version instead of constructing records through raw template interpolation.
-
Add regression tests covering carriage returns, line feeds,
BEGIN:VCARD,END:VCARD, semicolons, commas, backslashes, non-string properties, missing fields, and malformed phone numbers. Verify that no attacker-controlled input can create a physical VCard property or record boundary.
-
