Back to skill

Security audit

Customer Segmentation

Security checks for vulnerabilities and agentic risk

Overview

This skill matches its customer-segmentation purpose, but it handles CRM credentials and customer data with under-scoped local file access, unvalidated API destinations, and plaintext persistence.

Review before installing in any environment with real OKKI CRM access. Use least-privilege OKKI credentials, verify config.json points only to the intended HTTPS OKKI endpoints, protect the external OKKI workspace and .env files, and restrict or encrypt the generated data, logs, token cache, backups, and reports. Run tag sync in dry-run first and use --limit or --customer before any broad --confirm operation.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/customer-data-collector.js:76
Finding
OAuth credentials and bearer tokens can be transmitted to unrestricted configured origins<![CDATA[ ## Vulnerability Details **File Location**: `scripts/customer-data-collector.js:76-89`, `scripts/customer-data-collector.js:107-130`, `scripts/tag-sync.js:79-92`, and `scripts/tag-sync.js:124-140` **Vulnerability Type**: Unvalidated security-sensitive network destination **Risk Level**: High ### Vulnerable Code `scripts/customer-data-collector.js:76-89`: ```js const config = getOkkiConfig(); const body = new URLSearchParams({ client_id: config.clientId, client_secret: config.clientSecret, grant_type: 'client_credentials', scope: config.scope }); const resp = await fetch(config.tokenUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: body.toString() }); ``` `scripts/customer-data-collector.js:107-130`: ```js const config = getOkkiConfig(); const baseUrl = config.baseUrl; if (!token) token = await getAccessToken(); let url = `${baseUrl}${urlPath}`; if (method === 'GET' && Object.keys(params).length > 0) { const qs = new URLSearchParams(params).toString(); url += `?${qs}`; } const options = { method, headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } }; if (method === 'POST') { options.body = JSON.stringify(params); } const resp = await fetch(url, options); ``` `scripts/tag-sync.js:79-92`: ```js const config = getOkkiConfig(); const body = new URLSearchParams({ client_id: config.clientId, client_secret: config.clientSecret, grant_type: 'client_credentials', scope: config.scope }); const resp = await fetch(`${config.baseUrl}/v1/oauth2/access_token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: body.toString() }); ``` `scripts/tag-sync.js:124-140`: ```js async function okkiGet(endpoint, token, baseUrl) { const resp = await fetch(`${baseUrl}${endpoint}`, { headers: { Authorization: token } }); if (!resp.ok) throw new Error(`OKKI GET ${endpoint} failed: ${resp.status}` ...[truncated 3218 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Implement a centralized URL validator for every credential-bearing request: - Parse URLs with `new URL(...)`. - Require `url.protocol === 'https:'`. - Allow only explicitly documented OKKI hostnames. - Reject usernames, passwords, fragments, nonstandard ports unless required, and malformed URLs. - Resolve and reject loopback, link-local, private, multicast, and cloud metadata addresses where applicable. 2. Validate origin consistency: - Require the token endpoint and API base URL to match approved origins. - Do not permit a token obtained for one origin to be sent to another. - Construct endpoint paths from a trusted base rather than accepting complete arbitrary URLs. 3. Control redirect behavior: - Use `redirect: 'error'` or manually validate every redirect target. - Never follow a cross-origin redirect while sending a client secret or authorization header. 4. Reduce credential scope: - Use separate least-privilege OAuth clients for read-only collection and tag-writing operations. - Limit scopes to required customer, order, interaction, and tag operations. - Rotate credentials if configuration integrity may have been compromised. 5. Protect configuration integrity: - Store the external configuration in a directory writable only by the Skill owner. - Validate ownership and permissions before reading it. - Prefer deployment-managed secret injection over loading a broad shared `.env` file. 6. Add tests that confirm rejection of HTTP, unknown hosts, embedded credentials, redirects, loopback addresses, and metadata-service destinations. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/customer-data-collector.js:94
Finding
Access tokens and sensitive CRM exports are stored in plaintext without explicit restrictive permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/customer-data-collector.js:94-96`, `scripts/customer-data-collector.js:278-294`, `scripts/tag-sync.js:96-102`, `scripts/tag-sync.js:434-454`, `scripts/scoring-engine.js:315-321`, and `scripts/strategy-output.js:305-308` **Vulnerability Type**: Insecure local storage of credentials and sensitive business data **Risk Level**: Medium ### Vulnerable Code `scripts/customer-data-collector.js:94-96`: ```js const tokenData = await resp.json(); tokenData.expires_at = Date.now() / 1000 + (tokenData.expires_in || 7200); fs.writeFileSync(TOKEN_CACHE_PATH, JSON.stringify(tokenData, null, 2)); ``` `scripts/customer-data-collector.js:278-294`: ```js if (!dryRun) { fs.mkdirSync(DATA_DIR, { recursive: true }); const outputPath = path.join(DATA_DIR, 'customers-raw.json'); fs.writeFileSync(outputPath, JSON.stringify(rawData, null, 2)); console.log(`[collector] 数据已写入: ${outputPath}`); // 更新 last-sync const syncInfo = { last_sync_at: new Date().toISOString(), companies_count: companies.length, orders_count: orders.length, trails_count: trails.length, api_calls: apiCallCount, duration_ms: Date.now() - startTime }; fs.writeFileSync(path.join(DATA_DIR, 'last-sync.json'), JSON.stringify(syncInfo, null, 2)); console.log(`[collector] 同步信息已更新: last-sync.json`); } ``` `scripts/tag-sync.js:96-102`: ```js const tokenData = { access_token: data.data?.access_token || data.access_token, expires_at: Date.now() / 1000 + (data.data?.expires_in || data.expires_in || 28800) }; fs.mkdirSync(path.dirname(TOKEN_CACHE_PATH), { recursive: true }); fs.writeFileSync(TOKEN_CACHE_PATH, JSON.stringify(tokenData, null, 2)); ``` `scripts/tag-sync.js:434-454`: ```js const dateStr = new Date().toISOString().slice(0, 10); const backupPath = path.join(DATA_DIR, `tag-backup-${dateStr}.json`); fs.mkdirSync(DATA_DIR, { recursive: true }); fs.writeFileSync(backupPath, JSON.stringify(backupData, null, 2 ...[truncated 3431 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Apply restrictive permissions explicitly: - Create credential and data directories with mode `0700`. - Create token caches and CRM data files with mode `0600`. - Verify permissions on pre-existing files and directories before reuse. - Set an appropriately restrictive process umask at deployment time. 2. Write sensitive files safely: - Write to a securely created temporary file in the same protected directory. - Set mode `0600`, flush as appropriate, and atomically rename it. - Avoid symlink-following and reject unexpected non-regular-file targets. 3. Minimize token retention: - Store only the access token and expiration time when caching is essential. - Do not cache the complete OAuth response. - Prefer an operating-system credential store or managed secret service. - Delete expired token caches and rotate exposed tokens. 4. Minimize CRM data: - Persist only fields required for scoring and synchronization. - Remove unused personal, contact, and free-text fields before serialization. - Consider encryption at rest for raw exports and backups. 5. Establish retention controls: - Define expiration periods for raw exports, previous scores, tag backups, logs, and reports. - Automatically purge obsolete files. - Exclude sensitive files from source control, package publication, generic CI artifacts, and broad backups. 6. Add startup checks that fail closed when sensitive directories or files are owned by an unexpected user or are accessible to group/other principals. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (21)

Ae1

High
Category
analysis-evasion
Content
| `customer-data-collector.js` | — | OKKI API 增量采集客户数据 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `customer-data-collector.js` | — | OKKI API 增量采集客户数据 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `scoring-engine.js` | — | 加权评分 + 自动分层(5 级) |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `scoring-engine.js` | — | 加权评分 + 自动分层(5 级) |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `scoring-engine.js` | — | 加权评分 + 自动分层(5 级) |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `tag-sync.js` | 16KB | OKKI 标签同步(⚠️ 安全机制齐全) |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `tag-sync.js` | 16KB | OKKI 标签同步(⚠️ 安全机制齐全) |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `tag-sync.js` | 16KB | OKKI 标签同步(⚠️ 安全机制齐全) |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `tag-sync.js` | 16KB | OKKI 标签同步(⚠️ 安全机制齐全) |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `strategy-output.js` | 13KB | 策略建议输出 + 升级机会识别 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `strategy-output.js` | 13KB | 策略建议输出 + 升级机会识别 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `strategy-output.js` | 13KB | 策略建议输出 + 升级机会识别 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Credential Access

High
Category
Privilege Escalation
Content
// ==================== 配置 ====================
const OKKI_WORKSPACE = process.env.OKKI_WORKSPACE || path.resolve(__dirname, '../../../xiaoman-okki');
const ENV_PATH = process.env.ENV_PATH || path.resolve(__dirname, '../../../.env');
const OKKI_CONFIG_PATH = path.join(OKKI_WORKSPACE, 'api/config.json');
const TOKEN_CACHE_PATH = path.join(OKKI_WORKSPACE, 'api/token.cache');
Confidence
91% confidence
Finding
The script explicitly loads a .env file from a parent directory and then reads CRM configuration and token cache files from an external workspace, giving this skill access to credentials and authentication material outside its own project boundary. In the context of a customer-data collection script, this is especially sensitive because it enables automated access to customer, order, and follow-up records and could expose secrets or facilitate broad data extraction if the script is run in a privileged environment.

Credential Access

High
Category
Privilege Escalation
Content
// ==================== OKKI 认证 ====================
const OKKI_WORKSPACE = process.env.OKKI_WORKSPACE || path.resolve(__dirname, '../../../xiaoman-okki');
const ENV_PATH = process.env.ENV_PATH || path.resolve(__dirname, '../../../.env');
const OKKI_CONFIG_PATH = path.join(OKKI_WORKSPACE, 'api/config.json');
const TOKEN_CACHE_PATH = path.join(OKKI_WORKSPACE, 'api/token.cache');
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The substantive instructions and operational guidance are written in Chinese, and the file does not offer an alternative language or indicate that the skill is intentionally limited to Chinese-speaking users or a China-specific compliance context. This can violate a language/locale policy when users are not given an opt-in or choice.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
This JSON file contains user-facing natural-language content entirely in Chinese, including labels and customer-segmentation strategy descriptions. Because the file does not indicate that the skill is region-specific or offer any language/locale opt-in, it appears to enforce a specific language by default, which matches the language-policy violation criteria.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The help text says sample mode is only for testing the workflow, but the implementation may still write to data/scores-current.json when that file does not already exist. This can create persistent output derived from synthetic data and pollute later runs, baselines, or downstream processes that trust the presence of that file.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
The file-level description and usage text state the skill generates strategy suggestions and summaries in Chinese, and later user-facing console/summary strings are also hard-coded in Chinese. There is no indication that users can choose another language or that the locale restriction is documented as a justified region-specific constraint.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
When `--sample` is used and the expected data file is missing, the script invokes `scoring-engine.js` via `execSync`, expanding this tool from a formatter/report generator into a process launcher. That creates an unnecessary execution path and inherits the current environment, so compromise or unexpected behavior in the sibling script can be triggered indirectly by running this script.

Intent-Code Divergence

Medium
Confidence
99% confidence
Finding
The script documents a 500ms safety delay between API requests, but it calls sleep(API_INTERVAL_MS) without defining API_INTERVAL_MS anywhere. In confirm mode this causes a runtime exception before synchronization proceeds, defeating the stated safeguard and making the implementation inconsistent with operator expectations; if the variable were later introduced from untrusted scope, rate limiting behavior could also become unpredictable.

Vague Triggers

Low
Confidence
85% confidence
Finding
This manifest file includes a natural-language description only in Chinese, but does not indicate that the skill is intended exclusively for Chinese-speaking users or offer any language/locale choice. That can violate language/locale policy expectations when users are not given opt-in or documentation for the constraint.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access, suspicious.exposed_secret_literal (+1 more)

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/strategy-output.js:260

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/customer-data-collector.js:21

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/tag-sync.js:33

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/customer-data-collector.js:79

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/tag-sync.js:82

Sensitive-looking file read is paired with a network send.

Warn
Code
suspicious.potential_exfiltration
Location
scripts/customer-data-collector.js:70

Sensitive-looking file read is paired with a network send.

Warn
Code
suspicious.potential_exfiltration
Location
scripts/tag-sync.js:73