T09 · Insecure Skill Coding Practices
- Location
CONFIG.md:18- Finding
Secrets Are Collected Through Agent Conversations and Persisted in Generic Memory
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This invoice-chasing skill has a coherent business purpose, but it asks the agent to handle accounting, email, and customer financial data with broad permissions and weakly defined storage controls.
Review carefully before installing. Use a dedicated accounting app/account and sender mailbox, avoid pasting real secrets into chat unless the host provides an encrypted credential vault, keep approval required for all sends, verify recipient data before enabling automation, and define retention/deletion rules for customer and payment history.
CONFIG.md:18Secrets Are Collected Through Agent Conversations and Persisted in Generic Memory
SKILL.md:768Sensitive Customer and Financial Records Are Retained Indefinitely in Agent Memory
CONFIG.md:123Default Integration Setup Requests Permissions Beyond the Core Monitoring Workflow
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
## Troubleshooting
**"Xero 401 Unauthorized"**
→ Access token expired. The skill auto-refreshes — if this persists, your
refresh token may have expired (60 days of inactivity). Re-authenticate:
send your bot "reconnect Xero" and follow the OAuth flow again.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
## Troubleshooting
**"Xero 401 Unauthorized"**
→ Access token expired. The skill auto-refreshes — if this persists, your
refresh token may have expired (60 days of inactivity). Re-authenticate:
send your bot "reconnect Xero" and follow the OAuth flow again.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
## Troubleshooting
**"Xero 401 Unauthorized"**
→ Access token expired. The skill auto-refreshes — if this persists, your
refresh token may have expired (60 days of inactivity). Re-authenticate:
send your bot "reconnect Xero" and follow the OAuth flow again.
The skill instructs storage of Xero client secrets and OAuth tokens in memory, which creates a concentrated credential target if memory is exposed, logged, or accessible to other skills/components. Compromise of these values grants access to financial records and potentially write capabilities in the accounting platform.
ACCOUNTING_PLATFORM # xero | quickbooks | both
XERO_CLIENT_ID # From your Xero app — developer.xero.com/app/manage
XERO_CLIENT_SECRET # From your Xero app — keep secret, never expose
XERO_ACCESS_TOKEN # OAuth access token — fetched automatically, auto-refreshed
XERO_REFRESH_TOKEN # OAuth refresh token — stored securely, never shown
XERO_TENANT_ID # Xero organisation ID — fetched automatically after auth
QB_CLIENT_ID # From your QuickBooks app — developer.intuit.com
The same risk applies to QuickBooks credentials and tokens: storing them in ordinary memory exposes high-value authentication material beyond the minimum necessary boundary. An attacker or unrelated component with memory access could impersonate the business and access or alter accounting data.
XERO_TENANT_ID # Xero organisation ID — fetched automatically after auth
QB_CLIENT_ID # From your QuickBooks app — developer.intuit.com
QB_CLIENT_SECRET # From your QuickBooks app — keep secret, never expose
QB_ACCESS_TOKEN # QuickBooks OAuth access token — auto-refreshed
QB_REFRESH_TOKEN # QuickBooks OAuth refresh token — stored securely
QB_REALM_ID # QuickBooks company ID — fetched automatically after auth
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
STRIPE_PAYMENT_LINK # Your Stripe payment link URL (if PAYMENT_LINK_ENABLED = true)
Never log or repeat access tokens or refresh tokens back to the user.
### Token refresh
Both Xero and QuickBooks OAuth tokens expire after 30 minutes (access token) and
Automatically refreshing access tokens using a stored refresh token before each API call implies persistent handling of sensitive bearer credentials inside the skill runtime. If the refresh token is exposed, an attacker can mint new access tokens repeatedly and maintain unauthorized accounting access.
Never log or repeat access tokens or refresh tokens back to the user.
### Token refresh
Both Xero and QuickBooks OAuth tokens expire after 30 minutes (access token) and
60 days (refresh token). The skill automatically refreshes access tokens using the
stored refresh token before each API call. If the refresh token has expired (after
60 days of inactivity), prompt the user to re-authenticate via CONFIG.md.
The design relies on long-lived refresh tokens retained across runs, creating durable credential persistence risk. A stolen refresh token provides ongoing access well beyond a single session, making compromise materially more severe than transient access-token exposure.
### Token refresh
Both Xero and QuickBooks OAuth tokens expire after 30 minutes (access token) and
60 days (refresh token). The skill automatically refreshes access tokens using the
stored refresh token before each API call. If the refresh token has expired (after
60 days of inactivity), prompt the user to re-authenticate via CONFIG.md.
The token refresh flow includes submitting client secret and refresh token material as part of OAuth exchange operations. While normal protocol behavior, in the context of this skill it confirms active handling of raw high-value secrets, which is dangerous if implemented through prompt-visible memory or logs.
Refresh access token if needed:
POST https://identity.xero.com/connect/token
Content-Type: application/x-www-form-urlencoded
The QuickBooks refresh flow similarly requires raw credential material, including a Basic authorization value derived from client ID and secret plus the refresh token. Exposure of any of these values can enable unauthorized token minting and access to the company's accounting environment.
Refresh QBO access token:
POST https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer
Authorization: Basic {base64(QB_CLIENT_ID:QB_CLIENT_SECRET)}
The guide instructs users to paste OAuth client secrets directly into the bot chat, creating an unnecessary credential exposure path. Even if the document claims secrets are stored 'securely in memory,' there is no warning about chat logging, retention, access controls, or safer alternatives, so compromise of the bot, logs, or operator environment could expose accounting platform credentials.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
After authentication, the skill fetches your Xero organisation ID automatically:
GET https://api.xero.com/connections
If you have multiple Xero organisations, it will ask which to use.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
After authentication, the skill fetches your Xero organisation ID automatically:
GET https://api.xero.com/connections
If you have multiple Xero organisations, it will ask which to use.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
After authentication, the skill fetches your Xero organisation ID automatically:
GET https://api.xero.com/connections
If you have multiple Xero organisations, it will ask which to use.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
After authentication, the skill fetches your Xero organisation ID automatically:
GET https://api.xero.com/connections
If you have multiple Xero organisations, it will ask which to use.
The email setup section asks the user to send an email app password to the bot and encourages credential reuse if already configured. This grants access to a mailbox and outbound email capability, which could enable account compromise, data exposure, phishing, or unauthorized sending if the bot or its logs are accessed.
The README promotes automated email and WhatsApp chasing tied to accounting data, but does not explicitly warn that customer contact details, invoice status, balances, and message content may be transmitted to third-party communication providers. In a debt-collection context, automated outreach to real customers raises privacy, consent, and misdelivery risks, and can cause unauthorized or inappropriate contact if configuration or recipient selection is wrong.
The document states that all correspondence will be in UK English, which imposes a language/locale choice unconditionally. This is a natural-language policy issue unless the skill offers the user a language choice or clearly documents a justified region-specific constraint with opt-in.
The skill defaults CHASE_ENABLED to true, allowing the system to send debt-collection communications on schedule automatically. Autonomous external messaging based on invoice state can misfire on stale data, disputes, or wrong recipients, causing harassment, privacy incidents, and legal/commercial harm.
PAYMENT_TERMS_DAYS # Default: 30 (days after invoice date payment is due)
CURRENCY # Default: GBP
CHASE_ENABLED # Default: true — automatically send chase emails on schedule
APPROVAL_REQUIRED # Default: true — owner approves each chase before sending
WHATSAPP_CHASE_THRESHOLD # Default: £500 — chase via WhatsApp for invoices above this
STOP_CHASE_ON_DISPUTE # Default: true — pause chasing if dispute flag is set
The skill enables automated outbound email and WhatsApp contact to third-party customer details by default, but does not require an explicit upfront consent/confirmation flow before external communications begin. Because these messages can affect customer relationships, send legal/payment demands, and disclose billing context, unintended or misconfigured sends create privacy, compliance, and reputational risk.
Persisting detailed chase logs in memory creates a durable record of customer names, invoice numbers, payment amounts, communication timing, and collection activity. If memory is accessible to other skills, operators, or logs, this becomes a privacy and business-sensitivity exposure with long retention value to an attacker.
After sending each chase:
chase_stage and last_chased in invoice_chase_queuechase_log:{
"invoice_id": "{id}",
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.
- 💰 invoice summary · 📧 chase email · ✅ paid · ⏸️ paused · 🔴 final demand · 📊 report · ⚠️ alert
- Always show amount in £ — never raw numbers without currency
- For Stage 4 (final demand), always flag as needing owner attention — never auto-send
without approval regardless of APPROVAL_REQUIRED setting
- When a payment comes in: celebrate it briefly — "✅ £2,100 received from Metro Retail!"
- When reporting aged debtors: be matter-of-fact, not alarming — state the facts and
suggest next action
Skill's behavior or capabilities extend beyond its stated purpose. Scope creep allows an agent to perform actions unrelated to its documented functionality, increasing the attack surface.
UK-focused: Late Payment of Commercial Debts Act 1998 awareness built in. Statutory
interest calculation included. All correspondence in UK English.
Not limited to product sellers — works for any UK business that invoices clients:
freelancers, agencies, consultants, tradespeople, and service businesses.
---
Skill's behavior or capabilities extend beyond its stated purpose. Scope creep allows an agent to perform actions unrelated to its documented functionality, increasing the attack surface.
UK-focused: Late Payment of Commercial Debts Act 1998 awareness built in. Statutory
interest calculation included. All correspondence in UK English.
Not limited to product sellers — works for any UK business that invoices clients:
freelancers, agencies, consultants, tradespeople, and service businesses.
---
No suspicious patterns detected.