Back to skill

Security audit

fintech-specialist

Security checks for vulnerabilities and agentic risk

Overview

This skill is not malware, but its financial code examples are presented as secure/compliant while containing serious unsafe patterns.

Review before installing. Use this skill only for conceptual fintech discussion, not as a source of production-ready payment or DeFi code. Any generated implementation should add real authentication and authorization, tenant boundaries, redacted logging, test-only payment credentials during development, SafeERC20-style token handling, complete collateral/health-factor logic, and independent security review before handling real funds or regulated data.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
references/examples.md:936
Finding
Missing Authentication and Authorization on Financial Operations and Reporting Endpoints<![CDATA[ ## Vulnerability Details **File Location**: `references/examples.md:936-955, 1002-1035` **Vulnerability Type**: Missing authentication, authorization, and tenant isolation **Risk Level**: High ### Vulnerable Code ```typescript async handlePayment(req: Request, res: Response, next: NextFunction) { try { // Rate limiting const clientId = req.ip || 'unknown'; if (!await this.rateLimiter.checkLimit(clientId)) { return res.status(429).json({ error: 'Too many requests', }); } // Process payment const result = await this.processor.processPayment(req.body); if (result.success) { res.status(200).json(result); } else { res.status(400).json(result); } } catch (error: any) { auditLogger.error('Payment API error', { error: error.message, stack: error.stack, }); res.status(500).json({ error: 'Internal server error', reference: Date.now(), }); } } async handleReconciliation(req: Request, res: Response) { try { const { startDate, endDate } = req.query; const result = await this.reconciliation.reconcileTransactions( new Date(startDate as string), new Date(endDate as string) ); res.status(200).json(result); } catch (error: any) { res.status(500).json({ error: error.message, }); } } async handleComplianceReport(req: Request, res: Response) { try { const { type, startDate, endDate } = req.query; const report = await this.compliance.generateRegulatoryReport( type as string, { start: new Date(startDate as string), end: new Date(endDate as string), } ); res.status(200).json(report); } catch (error: any) { res.status(500).json({ ...[truncated 2099 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require authentication middleware on every payment, reconciliation, and compliance route. 2. Apply explicit role-based or attribute-based authorization: - Restrict payment initiation to authorized account owners or payment operators. - Restrict reconciliation reports to finance personnel. - Restrict regulatory reports to designated compliance roles. 3. Enforce tenant and account boundaries in every database query rather than filtering only after retrieval. 4. Verify server-side ownership of customer IDs, Stripe customer IDs, and payment-method IDs. 5. Use narrowly scoped service credentials and separate credentials for payment, reporting, and administrative operations. 6. Restrict report date ranges and permitted report types, and validate all query parameters. 7. Add authorization-denial tests covering anonymous users, cross-tenant access, and insufficient roles. 8. Retain rate limiting as defense in depth, but do not treat it as authentication or authorization. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
references/examples.md:1435
Finding
Collateral Withdrawal Solvency Check Always Returns True<![CDATA[ ## Vulnerability Details **File Location**: `references/examples.md:1183-1196, 1435-1443` **Vulnerability Type**: Broken financial invariant and incomplete health-factor validation **Risk Level**: Critical ### Vulnerable Code ```solidity function withdraw(address asset, uint256 amount) external nonReentrant { require(deposits[msg.sender][asset] >= amount, "Insufficient balance"); // Check if withdrawal maintains health factor require( _checkHealthFactor(msg.sender, asset, amount, true), "Unhealthy position" ); // Update balances deposits[msg.sender][asset] = deposits[msg.sender][asset].sub(amount); totalDeposits[asset] = totalDeposits[asset].sub(amount); // Transfer tokens to user IERC20(asset).transfer(msg.sender, amount); emit Withdraw(msg.sender, asset, amount); } ``` ```solidity function _checkHealthFactor( address user, address asset, uint256 amount, bool isWithdrawal ) internal view returns (bool) { // Simulate the action and check resulting health // Implementation details... return true; } ``` ### Technical Analysis The withdrawal function relies on `_checkHealthFactor` as its only protection against removing collateral that backs an outstanding loan. That function unconditionally returns `true`, so the intended solvency requirement is never enforced. A user can therefore borrow assets and subsequently withdraw collateral even when the resulting position would be undercollateralized. The existing `_isHealthy` function does not mitigate the issue because `withdraw` does not call it after applying or simulating the balance change. This is a direct violation of a lending protocol's core collateralization invariant. Marking the function as a placeholder does not make it safe as a deployable example, particularly because the surrounding code is presented as an advanced DeFi implementation. ### Attack Path 1. The attacker deposits a supported co ...[truncated 931 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the placeholder with a complete post-action solvency calculation. 2. Simulate the user's collateral balance after subtracting the proposed withdrawal. 3. Convert all remaining collateral and debt into a consistent unit using validated oracle prices and token decimals. 4. Apply per-asset collateral factors and require the resulting health factor to remain above the liquidation threshold. 5. Reject zero, stale, anomalous, or otherwise invalid oracle prices. 6. Accrue all relevant interest before evaluating debt and collateral. 7. Add invariant tests proving that no successful withdrawal can leave an account below the required collateral ratio. 8. Add tests for: - Full and partial collateral withdrawal. - Multiple collateral and borrowed assets. - Price changes and stale prices. - Active interest accrual. - Boundary conditions at the liquidation threshold. 9. Do not present or deploy the contract as production-ready while financial-security functions remain placeholders. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
references/examples.md:1157
Finding
Unsafe ERC-20 Transfers Can Create Unbacked Accounting Balances<![CDATA[ ## Vulnerability Details **File Location**: `references/examples.md:1157-1176, 1183-1196, 1210-1227, 1236-1252, 1280-1300` **Vulnerability Type**: Unchecked token transfer results and incompatible token accounting **Risk Level**: High ### Vulnerable Code ```solidity function deposit(address asset, uint256 amount) external nonReentrant { require(supportedAssets[asset], "Asset not supported"); require(amount > 0, "Amount must be greater than 0"); // Transfer tokens from user IERC20(asset).transferFrom(msg.sender, address(this), amount); // Update user balance deposits[msg.sender][asset] = deposits[msg.sender][asset].add(amount); totalDeposits[asset] = totalDeposits[asset].add(amount); // Mint interest-bearing tokens (simplified) _updateExchangeRate(asset); emit Deposit(msg.sender, asset, amount); } ``` The same unchecked pattern appears in withdrawal, borrowing, repayment, and liquidation: ```solidity IERC20(asset).transfer(msg.sender, amount); IERC20(asset).transferFrom(msg.sender, address(this), repayAmount); IERC20(borrowAsset).transferFrom(msg.sender, address(this), liquidationAmount); IERC20(collateralAsset).transfer(msg.sender, collateralToSeize); ``` ### Technical Analysis The contract calls `transfer` and `transferFrom` directly and ignores their Boolean return values. Some ERC-20 tokens return `false` instead of reverting on failure, while some non-standard implementations return no value. Fee-on-transfer and rebasing tokens can also cause the actual balance change to differ from the requested amount. During deposit, the contract credits the full requested amount immediately after the call. If the token returns `false` or transfers less than requested, the internal deposit balance can exceed the tokens actually received. The inflated deposit can then contribute to borrowing power. The owner-controlled `addAsset` function can register arbitrary token addresses, so token compatibility m ...[truncated 1278 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use OpenZeppelin `SafeERC20` and its `safeTransfer` and `safeTransferFrom` functions. 2. For deposits and repayments, measure the contract's token balance before and after transfer and credit only the amount actually received. 3. Explicitly reject fee-on-transfer, rebasing, callback-enabled, or otherwise unsupported token types unless the accounting model is designed for them. 4. Validate token behavior as part of asset onboarding. 5. Update internal accounting only after the relevant token operation has been verified. 6. Apply checks-effects-interactions ordering while retaining reentrancy protection. 7. Add tests for tokens that: - Return `false`. - Return no value. - Charge transfer fees. - Rebase balances. - Revert selectively. 8. Reconcile aggregate accounting against actual contract balances and halt affected markets when an inconsistency is detected. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
references/examples.md:396
Finding
Caller-Controlled Payment Metadata Is Written to Audit Logs Without Redaction<![CDATA[ ## Vulnerability Details **File Location**: `references/examples.md:91-97, 390-400` **Vulnerability Type**: Sensitive information exposure through application logging **Risk Level**: Medium ### Vulnerable Code The payment schema accepts arbitrary string metadata: ```typescript const PaymentRequestSchema = z.object({ amount: z.number().positive().max(999999.99), currency: z.enum(['USD', 'EUR', 'GBP', 'JPY']), customerId: z.string().uuid(), paymentMethod: z.enum(['card', 'bank_transfer', 'wallet', 'crypto']), metadata: z.record(z.string()).optional(), idempotencyKey: z.string().uuid(), }); ``` On processing failure, the complete validated request is logged: ```typescript } catch (error: any) { await client.query('ROLLBACK'); auditLogger.error('Payment processing failed', { error: error.message, request: validatedRequest, }); return { success: false, transactionId: '', status: 'failed', error: error.message, }; } ``` ### Technical Analysis The schema permits arbitrary metadata keys and values. Although card details are not part of `PaymentRequestSchema`, callers can place personal data, account information, authentication material, business secrets, or payment-related identifiers inside `metadata`. When payment processing fails, the entire validated request is written to both an append-only file and the console through the configured Winston transports. Console output is commonly forwarded to centralized logging services, expanding the number of systems and users that can access the data. The append-only configuration does not make the log immutable and does not provide confidentiality. It can instead increase retention of sensitive data. ### Attack Path 1. A caller submits a syntactically valid payment request containing sensitive information in arbitrary metadata fields. 2. The caller causes a later processing operation to fail, such as by using un ...[truncated 818 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Never log the complete payment request. 2. Log only a minimal allowlist such as transaction ID, normalized error code, payment method, and a correlation ID. 3. Define an explicit schema and key allowlist for metadata instead of accepting an unrestricted string record. 4. Reject metadata keys associated with credentials, card data, bank data, tokens, or other sensitive information. 5. Add centralized structured-log redaction for secrets, personal data, and financial identifiers. 6. Restrict access to audit and application logs using least-privilege roles. 7. Encrypt log transport and storage, define retention limits, and securely delete expired records. 8. Add automated tests verifying that sensitive fields never appear in file, console, or centralized logs. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (7)

Ae1

High
Category
analysis-evasion
Content
> 📎 **Code example 1** (typescript) — see [references/examples.md](references/examples.md)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
> 📎 **Code example 1** (typescript) — see [references/examples.md](references/examples.md)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
> 📎 **Code example 1** (typescript) — see [references/examples.md](references/examples.md)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This is a markdown file, so SQP-2 applies to omissions in the skill description itself. The file presents example code that accesses credentials, processes payment details, writes audit logs, stores transaction data, and sends data to external services such as Stripe, but the surrounding markdown includes no warning about privacy, financial impact, or system/data handling implications.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The manifest describes a specialist for payment systems, regulatory compliance, security/fraud prevention, and financial technologies, but this file's second example is a full decentralized finance protocol with collateralized deposits, borrowing, liquidation, and oracle administration. That is a materially different operational domain from the payment-processing and compliance focus presented in the manifest.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The contract advertises emergency pause capability but does not inherit from or implement pausable behavior, so `_pause()` and `_unpause()` are non-functional/undefined. In a live DeFi protocol, this creates a dangerous false sense of recoverability during exploits or market emergencies, delaying response while users and operators assume they can halt the system.

Intent-Code Divergence

Low
Confidence
95% confidence
Finding
Labeling ordinary append-mode file logging as 'immutable records' can cause operators and downstream users to rely on audit evidence that is in fact mutable. In a fintech/payment context, that can undermine incident response, fraud investigations, compliance attestations, and non-repudiation because local files and console output can be altered or deleted by an attacker or privileged insider.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
references/examples.md:434