fix: add input validation and safe JSON parsing across 13 CLIs

Add missing parameter validation to prevent /path/undefined API calls:
- dub: require --url for links create
- google-search-console: require --sitemap-url for sitemaps submit
- kit: validate IDs and emails for subscribers, forms, sequences, tags, broadcasts
- mailchimp: validate IDs for lists get, campaigns get/create/send, members add, reports get
- resend: validate --from/--to/--subject for send, audience/contact IDs for contacts
- rewardful: validate IDs for affiliates get/update, commissions get, links create
- semrush: require --domain/--phrase for all domain and keyword commands
- sendgrid: validate --from/--to/--subject for send, campaign IDs, email for validate

Wrap bare JSON.parse() calls in try/catch for user-provided JSON:
- dub (--links), ga4 (--params), kit (--fields x4), mixpanel (--properties x2),
  onesignal (--filters), paddle (--scheduled-change, --items x2),
  resend (--emails, --variables x2), segment (--properties, --traits, --events),
  sendgrid (--template-data)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Corey Haines 2026-02-17 22:44:06 -08:00
parent 51bdf2f6b3
commit 8eaff5e29f
13 changed files with 103 additions and 20 deletions

View file

@ -58,6 +58,7 @@ async function main() {
case 'links': case 'links':
switch (sub) { switch (sub) {
case 'create': { case 'create': {
if (!args.url) { result = { error: '--url required' }; break }
const body = {} const body = {}
if (args.url) body.url = args.url if (args.url) body.url = args.url
if (args.domain) body.domain = args.domain if (args.domain) body.domain = args.domain
@ -95,7 +96,12 @@ async function main() {
result = await api('DELETE', `/links/${args.id}`) result = await api('DELETE', `/links/${args.id}`)
break break
case 'bulk-create': { case 'bulk-create': {
const links = JSON.parse(args.links || '[]') let links
try {
links = JSON.parse(args.links || '[]')
} catch {
result = { error: 'Invalid JSON in --links' }; break
}
result = await api('POST', '/links/bulk', links) result = await api('POST', '/links/bulk', links)
break break
} }

View file

@ -150,7 +150,14 @@ async function main() {
if (!args['api-secret']) { result = { error: '--api-secret required' }; break } if (!args['api-secret']) { result = { error: '--api-secret required' }; break }
if (!args['client-id']) { result = { error: '--client-id required' }; break } if (!args['client-id']) { result = { error: '--client-id required' }; break }
if (!args['event-name']) { result = { error: '--event-name required' }; break } if (!args['event-name']) { result = { error: '--event-name required' }; break }
const eventParams = args.params ? JSON.parse(args.params) : {} let eventParams = {}
if (args.params) {
try {
eventParams = JSON.parse(args.params)
} catch {
result = { error: 'Invalid JSON in --params' }; break
}
}
const body = { const body = {
client_id: args['client-id'], client_id: args['client-id'],
events: [{ events: [{

View file

@ -129,6 +129,7 @@ async function main() {
result = await api('GET', `/webmasters/v3/sites/${encodedSiteUrl}/sitemaps`) result = await api('GET', `/webmasters/v3/sites/${encodedSiteUrl}/sitemaps`)
break break
case 'submit': { case 'submit': {
if (!args['sitemap-url']) { result = { error: '--sitemap-url required' }; break }
const sitemapUrl = encodeURIComponent(args['sitemap-url']) const sitemapUrl = encodeURIComponent(args['sitemap-url'])
result = await api('PUT', `/webmasters/v3/sites/${encodedSiteUrl}/sitemaps/${sitemapUrl}`) result = await api('PUT', `/webmasters/v3/sites/${encodedSiteUrl}/sitemaps/${sitemapUrl}`)
if (!result.body && !result.error) { if (!result.body && !result.error) {

View file

@ -91,12 +91,16 @@ async function main() {
break break
} }
case 'get': case 'get':
if (!rest[0]) { result = { error: 'Subscriber ID required' }; break }
result = await api('GET', `/subscribers/${rest[0]}`) result = await api('GET', `/subscribers/${rest[0]}`)
break break
case 'update': { case 'update': {
if (!rest[0]) { result = { error: 'Subscriber ID required' }; break }
const body = {} const body = {}
if (args['first-name']) body.first_name = args['first-name'] if (args['first-name']) body.first_name = args['first-name']
if (args.fields) body.fields = JSON.parse(args.fields) if (args.fields) {
try { body.fields = JSON.parse(args.fields) } catch { result = { error: 'Invalid JSON in --fields' }; break }
}
result = await api('PUT', `/subscribers/${rest[0]}`, body) result = await api('PUT', `/subscribers/${rest[0]}`, body)
break break
} }
@ -116,10 +120,14 @@ async function main() {
result = await api('GET', '/forms', null, false) result = await api('GET', '/forms', null, false)
break break
case 'subscribe': { case 'subscribe': {
if (!rest[0]) { result = { error: 'Form ID required' }; break }
if (!args.email) { result = { error: '--email required' }; break }
const formId = rest[0] const formId = rest[0]
const body = { email: args.email } const body = { email: args.email }
if (args['first-name']) body.first_name = args['first-name'] if (args['first-name']) body.first_name = args['first-name']
if (args.fields) body.fields = JSON.parse(args.fields) if (args.fields) {
try { body.fields = JSON.parse(args.fields) } catch { result = { error: 'Invalid JSON in --fields' }; break }
}
result = await api('POST', `/forms/${formId}/subscribe`, body, false) result = await api('POST', `/forms/${formId}/subscribe`, body, false)
break break
} }
@ -134,10 +142,14 @@ async function main() {
result = await api('GET', '/sequences', null, false) result = await api('GET', '/sequences', null, false)
break break
case 'subscribe': { case 'subscribe': {
if (!rest[0]) { result = { error: 'Sequence ID required' }; break }
if (!args.email) { result = { error: '--email required' }; break }
const sequenceId = rest[0] const sequenceId = rest[0]
const body = { email: args.email } const body = { email: args.email }
if (args['first-name']) body.first_name = args['first-name'] if (args['first-name']) body.first_name = args['first-name']
if (args.fields) body.fields = JSON.parse(args.fields) if (args.fields) {
try { body.fields = JSON.parse(args.fields) } catch { result = { error: 'Invalid JSON in --fields' }; break }
}
result = await api('POST', `/sequences/${sequenceId}/subscribe`, body, false) result = await api('POST', `/sequences/${sequenceId}/subscribe`, body, false)
break break
} }
@ -152,16 +164,22 @@ async function main() {
result = await api('GET', '/tags', null, false) result = await api('GET', '/tags', null, false)
break break
case 'subscribe': { case 'subscribe': {
if (!rest[0]) { result = { error: 'Tag ID required' }; break }
if (!args.email) { result = { error: '--email required' }; break }
const tagId = rest[0] const tagId = rest[0]
const body = { email: args.email } const body = { email: args.email }
if (args['first-name']) body.first_name = args['first-name'] if (args['first-name']) body.first_name = args['first-name']
if (args.fields) body.fields = JSON.parse(args.fields) if (args.fields) {
try { body.fields = JSON.parse(args.fields) } catch { result = { error: 'Invalid JSON in --fields' }; break }
}
result = await api('POST', `/tags/${tagId}/subscribe`, body, false) result = await api('POST', `/tags/${tagId}/subscribe`, body, false)
break break
} }
case 'remove': { case 'remove': {
if (!rest[0]) { result = { error: 'Tag ID required' }; break }
const tagId = rest[0] const tagId = rest[0]
const subscriberId = rest[1] || args['subscriber-id'] const subscriberId = rest[1] || args['subscriber-id']
if (!subscriberId) { result = { error: 'Subscriber ID required' }; break }
result = await api('DELETE', `/subscribers/${subscriberId}/tags/${tagId}`) result = await api('DELETE', `/subscribers/${subscriberId}/tags/${tagId}`)
break break
} }
@ -178,6 +196,7 @@ async function main() {
break break
} }
case 'create': { case 'create': {
if (!args.subject || !args.content) { result = { error: '--subject and --content required' }; break }
const body = { const body = {
subject: args.subject, subject: args.subject,
content: args.content, content: args.content,

View file

@ -69,6 +69,7 @@ async function main() {
break break
} }
case 'get': case 'get':
if (!rest[0]) { result = { error: 'List ID required' }; break }
result = await api('GET', `/lists/${rest[0]}`) result = await api('GET', `/lists/${rest[0]}`)
break break
default: default:
@ -91,6 +92,8 @@ async function main() {
break break
} }
case 'add': { case 'add': {
if (!rest[0]) { result = { error: 'List ID required' }; break }
if (!args.email) { result = { error: '--email required' }; break }
if (!args['list-id']) { if (!args['list-id']) {
result = { error: '--list-id is required for members add' } result = { error: '--list-id is required for members add' }
break break
@ -142,9 +145,11 @@ async function main() {
break break
} }
case 'get': case 'get':
if (!rest[0]) { result = { error: 'Campaign ID required' }; break }
result = await api('GET', `/campaigns/${rest[0]}`) result = await api('GET', `/campaigns/${rest[0]}`)
break break
case 'create': { case 'create': {
if (!args['list-id']) { result = { error: '--list-id required' }; break }
const body = { const body = {
type: args.type || 'regular', type: args.type || 'regular',
recipients: { recipients: {
@ -160,6 +165,7 @@ async function main() {
break break
} }
case 'send': case 'send':
if (!rest[0]) { result = { error: 'Campaign ID required' }; break }
result = await api('POST', `/campaigns/${rest[0]}/actions/send`) result = await api('POST', `/campaigns/${rest[0]}/actions/send`)
break break
default: default:
@ -170,6 +176,7 @@ async function main() {
case 'reports': case 'reports':
switch (sub) { switch (sub) {
case 'get': case 'get':
if (!rest[0]) { result = { error: 'Campaign ID required' }; break }
result = await api('GET', `/reports/${rest[0]}`) result = await api('GET', `/reports/${rest[0]}`)
break break
default: default:

View file

@ -118,7 +118,8 @@ async function main() {
if (!TOKEN) { result = { error: 'MIXPANEL_TOKEN required for tracking' }; break } if (!TOKEN) { result = { error: 'MIXPANEL_TOKEN required for tracking' }; break }
if (!args['distinct-id']) { result = { error: '--distinct-id required' }; break } if (!args['distinct-id']) { result = { error: '--distinct-id required' }; break }
if (!args.event) { result = { error: '--event required' }; break } if (!args.event) { result = { error: '--event required' }; break }
const properties = args.properties ? JSON.parse(args.properties) : {} let properties
try { properties = args.properties ? JSON.parse(args.properties) : {} } catch { result = { error: 'Invalid JSON in --properties' }; break }
properties.token = TOKEN properties.token = TOKEN
properties.distinct_id = args['distinct-id'] properties.distinct_id = args['distinct-id']
result = await ingestApi('POST', '/track', [{ result = await ingestApi('POST', '/track', [{
@ -137,7 +138,8 @@ async function main() {
case 'set': { case 'set': {
if (!TOKEN) { result = { error: 'MIXPANEL_TOKEN required for profiles' }; break } if (!TOKEN) { result = { error: 'MIXPANEL_TOKEN required for profiles' }; break }
if (!args['distinct-id']) { result = { error: '--distinct-id required' }; break } if (!args['distinct-id']) { result = { error: '--distinct-id required' }; break }
const properties = args.properties ? JSON.parse(args.properties) : {} let properties
try { properties = args.properties ? JSON.parse(args.properties) : {} } catch { result = { error: 'Invalid JSON in --properties' }; break }
result = await ingestApi('POST', '/engage', [{ result = await ingestApi('POST', '/engage', [{
$token: TOKEN, $token: TOKEN,
$distinct_id: args['distinct-id'], $distinct_id: args['distinct-id'],

View file

@ -130,7 +130,8 @@ async function main() {
case 'create': { case 'create': {
const name = args.name const name = args.name
if (!name) { result = { error: '--name required' }; break } if (!name) { result = { error: '--name required' }; break }
const filters = args.filters ? JSON.parse(args.filters) : [{ field: 'session_count', relation: '>', value: '0' }] let filters
try { filters = args.filters ? JSON.parse(args.filters) : [{ field: 'session_count', relation: '>', value: '0' }] } catch { result = { error: 'Invalid JSON in --filters' }; break }
result = await api('POST', `/api/v1/apps/${APP_ID}/segments`, { name, filters }) result = await api('POST', `/api/v1/apps/${APP_ID}/segments`, { name, filters })
break break
} }

View file

@ -203,7 +203,9 @@ async function main() {
if (!id) { result = { error: '--id required' }; break } if (!id) { result = { error: '--id required' }; break }
const body = {} const body = {}
if (args['proration-billing-mode']) body.proration_billing_mode = args['proration-billing-mode'] if (args['proration-billing-mode']) body.proration_billing_mode = args['proration-billing-mode']
if (args['scheduled-change']) body.scheduled_change = JSON.parse(args['scheduled-change']) if (args['scheduled-change']) {
try { body.scheduled_change = JSON.parse(args['scheduled-change']) } catch { result = { error: 'Invalid JSON in --scheduled-change' }; break }
}
result = await api('PATCH', `/subscriptions/${id}`, body) result = await api('PATCH', `/subscriptions/${id}`, body)
break break
} }
@ -248,7 +250,9 @@ async function main() {
case 'create': { case 'create': {
const items = args.items const items = args.items
if (!items) { result = { error: '--items required (JSON array of {price_id, quantity})' }; break } if (!items) { result = { error: '--items required (JSON array of {price_id, quantity})' }; break }
const body = { items: JSON.parse(items) } let parsedItems
try { parsedItems = JSON.parse(items) } catch { result = { error: 'Invalid JSON in --items' }; break }
const body = { items: parsedItems }
if (args['customer-id']) body.customer_id = args['customer-id'] if (args['customer-id']) body.customer_id = args['customer-id']
result = await api('POST', '/transactions', body) result = await api('POST', '/transactions', body)
break break
@ -304,11 +308,13 @@ async function main() {
if (!action) { result = { error: '--action required (refund, credit, chargeback)' }; break } if (!action) { result = { error: '--action required (refund, credit, chargeback)' }; break }
if (!reason) { result = { error: '--reason required' }; break } if (!reason) { result = { error: '--reason required' }; break }
if (!items) { result = { error: '--items required (JSON array of {item_id, type, amount})' }; break } if (!items) { result = { error: '--items required (JSON array of {item_id, type, amount})' }; break }
let parsedItems
try { parsedItems = JSON.parse(items) } catch { result = { error: 'Invalid JSON in --items' }; break }
result = await api('POST', '/adjustments', { result = await api('POST', '/adjustments', {
transaction_id: transactionId, transaction_id: transactionId,
action, action,
reason, reason,
items: JSON.parse(items), items: parsedItems,
}) })
break break
} }

View file

@ -56,6 +56,7 @@ async function main() {
switch (cmd) { switch (cmd) {
case 'send': { case 'send': {
if (!args.from || !args.to || !args.subject) { result = { error: '--from, --to, and --subject required' }; break }
const body = { from: args.from, to: args.to?.split(','), subject: args.subject } const body = { from: args.from, to: args.to?.split(','), subject: args.subject }
if (args.html) body.html = args.html if (args.html) body.html = args.html
if (args.text) body.text = args.text if (args.text) body.text = args.text
@ -156,6 +157,7 @@ async function main() {
case 'contacts': { case 'contacts': {
const audienceId = sub const audienceId = sub
if (!audienceId) { result = { error: 'Audience ID required as subcommand arg' }; break }
const action = rest[0] const action = rest[0]
const contactId = rest[1] const contactId = rest[1]
switch (action) { switch (action) {
@ -166,6 +168,7 @@ async function main() {
break break
} }
case 'get': case 'get':
if (!rest[1]) { result = { error: 'Contact ID required' }; break }
result = await api('GET', `/audiences/${audienceId}/contacts/${contactId}`) result = await api('GET', `/audiences/${audienceId}/contacts/${contactId}`)
break break
case 'create': { case 'create': {
@ -177,6 +180,7 @@ async function main() {
break break
} }
case 'update': { case 'update': {
if (!rest[1]) { result = { error: 'Contact ID required' }; break }
const body = {} const body = {}
if (args['first-name']) body.first_name = args['first-name'] if (args['first-name']) body.first_name = args['first-name']
if (args['last-name']) body.last_name = args['last-name'] if (args['last-name']) body.last_name = args['last-name']
@ -185,6 +189,7 @@ async function main() {
break break
} }
case 'delete': case 'delete':
if (!rest[1]) { result = { error: 'Contact ID required' }; break }
result = await api('DELETE', `/audiences/${audienceId}/contacts/${contactId}`) result = await api('DELETE', `/audiences/${audienceId}/contacts/${contactId}`)
break break
default: default:
@ -216,7 +221,12 @@ async function main() {
break break
case 'batch': { case 'batch': {
const emails = JSON.parse(args.emails || '[]') let emails
try {
emails = JSON.parse(args.emails || '[]')
} catch (e) {
result = { error: 'Invalid JSON for --emails: ' + e.message }; break
}
result = await api('POST', '/emails/batch', emails) result = await api('POST', '/emails/batch', emails)
break break
} }
@ -241,7 +251,9 @@ async function main() {
if (args.subject) body.subject = args.subject if (args.subject) body.subject = args.subject
if (args['reply-to']) body.reply_to = args['reply-to'] if (args['reply-to']) body.reply_to = args['reply-to']
if (args.text) body.text = args.text if (args.text) body.text = args.text
if (args.variables) body.variables = JSON.parse(args.variables) if (args.variables) {
try { body.variables = JSON.parse(args.variables) } catch (e) { result = { error: 'Invalid JSON for --variables: ' + e.message }; break }
}
result = await api('POST', '/templates', body) result = await api('POST', '/templates', body)
break break
} }
@ -254,7 +266,9 @@ async function main() {
if (args.subject) body.subject = args.subject if (args.subject) body.subject = args.subject
if (args['reply-to']) body.reply_to = args['reply-to'] if (args['reply-to']) body.reply_to = args['reply-to']
if (args.text) body.text = args.text if (args.text) body.text = args.text
if (args.variables) body.variables = JSON.parse(args.variables) if (args.variables) {
try { body.variables = JSON.parse(args.variables) } catch (e) { result = { error: 'Invalid JSON for --variables: ' + e.message }; break }
}
result = await api('PATCH', `/templates/${rest[0]}`, body) result = await api('PATCH', `/templates/${rest[0]}`, body)
break break
} }

View file

@ -65,6 +65,7 @@ async function main() {
break break
} }
case 'get': case 'get':
if (!rest[0]) { result = { error: 'Affiliate ID required' }; break }
result = await api('GET', `/affiliates/${rest[0]}`) result = await api('GET', `/affiliates/${rest[0]}`)
break break
case 'search': { case 'search': {
@ -74,6 +75,7 @@ async function main() {
break break
} }
case 'update': { case 'update': {
if (!rest[0]) { result = { error: 'Affiliate ID required' }; break }
const body = {} const body = {}
if (args['first-name']) body.first_name = args['first-name'] if (args['first-name']) body.first_name = args['first-name']
if (args['last-name']) body.last_name = args['last-name'] if (args['last-name']) body.last_name = args['last-name']
@ -114,6 +116,7 @@ async function main() {
break break
} }
case 'get': case 'get':
if (!rest[0]) { result = { error: 'Commission ID required' }; break }
result = await api('GET', `/commissions/${rest[0]}`) result = await api('GET', `/commissions/${rest[0]}`)
break break
default: default:
@ -124,6 +127,7 @@ async function main() {
case 'links': case 'links':
switch (sub) { switch (sub) {
case 'create': { case 'create': {
if (!args['affiliate-id']) { result = { error: '--affiliate-id required' }; break }
const body = {} const body = {}
if (args.token) body.token = args.token if (args.token) body.token = args.token
if (args.url) body.url = args.url if (args.url) body.url = args.url

View file

@ -93,7 +93,9 @@ async function main() {
userId: args['user-id'], userId: args['user-id'],
event: args.event, event: args.event,
} }
if (args.properties) body.properties = JSON.parse(args.properties) if (args.properties) {
try { body.properties = JSON.parse(args.properties) } catch { result = { error: 'Invalid JSON in --properties' }; break }
}
result = await trackApi('POST', '/track', body) result = await trackApi('POST', '/track', body)
break break
} }
@ -107,7 +109,9 @@ async function main() {
case 'user': { case 'user': {
if (!args['user-id']) { result = { error: '--user-id required' }; break } if (!args['user-id']) { result = { error: '--user-id required' }; break }
const body = { userId: args['user-id'] } const body = { userId: args['user-id'] }
if (args.traits) body.traits = JSON.parse(args.traits) if (args.traits) {
try { body.traits = JSON.parse(args.traits) } catch { result = { error: 'Invalid JSON in --traits' }; break }
}
result = await trackApi('POST', '/identify', body) result = await trackApi('POST', '/identify', body)
break break
} }
@ -122,7 +126,9 @@ async function main() {
if (!args['user-id']) { result = { error: '--user-id required' }; break } if (!args['user-id']) { result = { error: '--user-id required' }; break }
const body = { userId: args['user-id'] } const body = { userId: args['user-id'] }
if (args.name) body.name = args.name if (args.name) body.name = args.name
if (args.properties) body.properties = JSON.parse(args.properties) if (args.properties) {
try { body.properties = JSON.parse(args.properties) } catch { result = { error: 'Invalid JSON in --properties' }; break }
}
result = await trackApi('POST', '/page', body) result = await trackApi('POST', '/page', body)
break break
} }
@ -135,7 +141,8 @@ async function main() {
switch (sub) { switch (sub) {
case 'send': { case 'send': {
if (!args.events) { result = { error: '--events required (JSON array)' }; break } if (!args.events) { result = { error: '--events required (JSON array)' }; break }
const batch = JSON.parse(args.events) let batch
try { batch = JSON.parse(args.events) } catch { result = { error: 'Invalid JSON in --events' }; break }
result = await trackApi('POST', '/batch', { batch }) result = await trackApi('POST', '/batch', { batch })
break break
} }

View file

@ -75,6 +75,7 @@ async function main() {
case 'domain': case 'domain':
switch (sub) { switch (sub) {
case 'overview': { case 'overview': {
if (!args.domain) { result = { error: '--domain required' }; break }
const params = new URLSearchParams({ const params = new URLSearchParams({
type: 'domain_ranks', type: 'domain_ranks',
export_columns: 'Db,Dn,Rk,Or,Ot,Oc,Ad,At,Ac', export_columns: 'Db,Dn,Rk,Or,Ot,Oc,Ad,At,Ac',
@ -84,6 +85,7 @@ async function main() {
break break
} }
case 'organic': { case 'organic': {
if (!args.domain) { result = { error: '--domain required' }; break }
const params = new URLSearchParams({ const params = new URLSearchParams({
type: 'domain_organic', type: 'domain_organic',
export_columns: 'Ph,Po,Pp,Pd,Nq,Cp,Ur,Tr,Tc,Co,Nr', export_columns: 'Ph,Po,Pp,Pd,Nq,Cp,Ur,Tr,Tc,Co,Nr',
@ -95,6 +97,7 @@ async function main() {
break break
} }
case 'competitors': { case 'competitors': {
if (!args.domain) { result = { error: '--domain required' }; break }
const params = new URLSearchParams({ const params = new URLSearchParams({
type: 'domain_organic_organic', type: 'domain_organic_organic',
export_columns: 'Dn,Cr,Np,Or,Ot,Oc,Ad', export_columns: 'Dn,Cr,Np,Or,Ot,Oc,Ad',
@ -113,6 +116,7 @@ async function main() {
case 'keywords': case 'keywords':
switch (sub) { switch (sub) {
case 'overview': { case 'overview': {
if (!args.phrase) { result = { error: '--phrase required' }; break }
const params = new URLSearchParams({ const params = new URLSearchParams({
type: 'phrase_all', type: 'phrase_all',
export_columns: 'Ph,Nq,Cp,Co,Nr', export_columns: 'Ph,Nq,Cp,Co,Nr',
@ -123,6 +127,7 @@ async function main() {
break break
} }
case 'related': { case 'related': {
if (!args.phrase) { result = { error: '--phrase required' }; break }
const params = new URLSearchParams({ const params = new URLSearchParams({
type: 'phrase_related', type: 'phrase_related',
export_columns: 'Ph,Nq,Cp,Co,Nr,Td', export_columns: 'Ph,Nq,Cp,Co,Nr,Td',
@ -134,6 +139,7 @@ async function main() {
break break
} }
case 'difficulty': { case 'difficulty': {
if (!args.phrase) { result = { error: '--phrase required' }; break }
const params = new URLSearchParams({ const params = new URLSearchParams({
type: 'phrase_kdi', type: 'phrase_kdi',
export_columns: 'Ph,Kd', export_columns: 'Ph,Kd',

View file

@ -56,6 +56,7 @@ async function main() {
switch (cmd) { switch (cmd) {
case 'send': { case 'send': {
if (!args.from || !args.to || !args.subject) { result = { error: '--from, --to, and --subject required' }; break }
const body = { const body = {
personalizations: [{ personalizations: [{
to: args.to.split(',').map(e => ({ email: e.trim() })), to: args.to.split(',').map(e => ({ email: e.trim() })),
@ -66,7 +67,7 @@ async function main() {
if (args['template-id']) { if (args['template-id']) {
body.template_id = args['template-id'] body.template_id = args['template-id']
if (args['template-data']) { if (args['template-data']) {
body.personalizations[0].dynamic_template_data = JSON.parse(args['template-data']) try { body.personalizations[0].dynamic_template_data = JSON.parse(args['template-data']) } catch (e) { result = { error: 'Invalid JSON for --template-data: ' + e.message }; break }
} }
} else { } else {
const content = [] const content = []
@ -117,6 +118,7 @@ async function main() {
break break
} }
case 'get': case 'get':
if (!rest[0]) { result = { error: 'Campaign ID required' }; break }
result = await api('GET', `/marketing/campaigns/${rest[0]}`) result = await api('GET', `/marketing/campaigns/${rest[0]}`)
break break
default: default:
@ -172,6 +174,7 @@ async function main() {
case 'validate': case 'validate':
switch (sub) { switch (sub) {
case 'email': { case 'email': {
if (!args.email && !rest[0]) { result = { error: '--email required' }; break }
const body = { email: args.email || rest[0] } const body = { email: args.email || rest[0] }
result = await api('POST', '/validations/email', body) result = await api('POST', '/validations/email', body)
break break