Back to skill

Security audit

定制化招标信息采集

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real procurement-monitoring tool, but it needs review because it browses user-supplied sites with weak containment and auto-loads local JavaScript adapters with broad privileges.

Review before installing. Run this only in a contained environment or dedicated low-privilege account, avoid custom adapters unless you trust and review their code, do not enable webhooks for sensitive opportunities without accepting third-party data exposure, and prefer a version that restores Chromium sandboxing, validates destinations, pins setup commands, and constrains plugin loading.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
Findings (6)

T01 · Skill Instruction Hijacking

Error
Location
SKILL.md:24
Finding

Mandatory Agent instruction override and persistent memory modification

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
src/detail-fetcher.js:159
Finding

Unrestricted browser navigation permits server-side request forgery

Content
View full analysis
registry.findTransformDetailUrl({}, url)); const fetchUrl = transformUrl(url); ``` ```js const page = await context.newPage(); const ok = await gotoWithRetry(page, fetchUrl, options.retryStairs); if (!ok) { result.error = 'Page load failed after retries'; await context.close(); return result; } ``` The CLI exposes this functionality directly: ```js if (opts.url) { // Test detail fetching const { fetchDetail } = require('../src/detail-fetcher'); console.log(`Fetching detail from: ${opts.url}`); const result = await fetchDetail(opts.url); console.log(JSON.stringify(result, null, 2)); } ``` ### Technical Analysis The validation only checks whether the initial URL uses HTTP or HTTPS. It does not reject: - Loopback addresses such as `127.0.0.1` or `::1` - RFC 1918 private networks - Link-local networks - Cloud instance metadata addresses - Internal DNS names - Redirects to restricted destinations - DNS rebinding to a private address The adapter transformation is applied after validation, and the resulting `fetchUrl` is not revalidated. Scanned, site-controlled detail URLs also enter this function through ...[truncated 1193 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
src/scanner.js:76
Finding

Chromium sandbox is disabled while visiting untrusted websites

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/platforms/registry.js:36
Finding

Custom adapters execute arbitrary JavaScript before validation

Content
View full analysis
f.endsWith('.js')); for (const file of userFiles) { try { const adapter = require(path.join(userDir, file)); if (isValidAdapter(adapter) && !seen.has(adapter.meta.id)) { adapters[adapter.meta.id] = adapter; seen.add(adapter.meta.id); } } catch (error) { console.error(`[registry] Failed to load user adapter ${file}: ${error.message}`); } } } ``` ```js // 3. Load custom paths from config const customPaths = config?.platforms?.custom_paths || []; for (const p of customPaths) { if (fs.existsSync(p) && p.endsWith('.js')) { try { const adapter = require(path.resolve(p)); if (isValidAdapter(adapter) && !seen.has(adapter.meta.id)) { adapters[adapter.meta.id] = adapter; seen.add(adapter.meta.id); } } catch (error) { console.error(`[registry] Failed to load custom adapter ${p}: ${error.message}`); } } } ``` ### Technical Analysis Node.js executes a module's top-level code immediately when `require()` is called. The subsequent `isValidAdapter()` check only validates the exported object after arbitrary module code has already executed. Every JavaScript file in the user adapter directory is loaded, even if it is not enabled for scanning. Configuration can also point `custom_paths` at any local `.js` file. Registry loading occurs during scans, platform lookup, listing, and detail URL transformation. The plugin feature is documented, but it provides unrestricted code execution rather than a least-privilege adapter interface. ### Attack Path 1. An attacker modi ...[truncated 829 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
bin/bidding-hunter.js:287
Finding

Adapter creation permits path traversal and generated JavaScript injection

Content
View full analysis
', 'Platform identifier (e.g., sichuan)') .action(async (opts) => { const template = fs.readFileSync( path.join(__dirname, '..', 'templates', 'platform-adapter.js'), 'utf8' ); const userDir = path.join(process.env.HOME || '/tmp', '.bidding-hunter', 'platforms'); fs.mkdirSync(userDir, { recursive: true }); const filePath = path.join(userDir, `${opts.name}.js`); if (fs.existsSync(filePath)) { console.error(`Adapter already exists: ${filePath}`); process.exit(1); } const content = template .replace(/\{\{PLATFORM_ID\}\}/g, opts.name) .replace(/\{\{PLATFORM_NAME\}\}/g, opts.name); fs.writeFileSync(filePath, content, 'utf8'); ``` The unescaped values are inserted into JavaScript string literals: ```js const adapter = new BasePlatformAdapter({ id: '{{PLATFORM_ID}}', name: '{{PLATFORM_NAME}}', url: 'https://example.gov.cn/', version: '1.0.0', }); ``` ### Technical Analysis The `--name` value is used as both a filesystem path component and raw JavaScript source without validation. Traversal sequences such as `../` can cause `path.join()` to resolve outside the intended adapter directory. The existing-file check prevents overwriting an existing target, but it does not prevent creation of a new `.js` file elsewhere. Quotes, backslashes, line breaks, and JavaScript syntax in the name are also inserted directly into quoted source literals. This can alter the generated program rather than simply assigning an adapter identifier. ### Attack Path 1. An attacker influences the argument passed to ...[truncated 860 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
package-lock.json:29
Finding

Committed dependency lockfile uses a third-party package mirror

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (59)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · .gitignore (reported line 32)May include surrounding context.

text
Thumbs.db

# Environment
.env
.env.local

# Coverage

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · .gitignore (reported line 33)May include surrounding context.

text
# Environment
.env
.env.local

# Coverage
coverage/

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The document claims the tool is not a scraper for arbitrary websites, but later provides an arbitrary-URL exploration workflow and user-added adapters. This contradiction is dangerous because it normalizes broader capabilities than advertised and can bypass operator expectations or governance controls based on the declared scope.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

Auto-discovering and loading JavaScript from built-in, user-home, and configurable custom paths creates an arbitrary code execution extension point. Any malicious or tampered adapter/notifier file in those locations would run with the privileges of the bidding-hunter process, enabling data exfiltration, filesystem access, or command execution.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared description presents a full procurement monitoring engine with automated scanning across multiple Chinese procurement platforms, configurable keyword matching, bid status tracking, deadline-aware reporting, and built-in adapters. The supplied code chunk does not implement those core behaviors. Instead, it is a developer/agent-oriented utility that prints step-by-step guidance for exploring a new platform and, optionally, uses Playwright to open one URL and collect structural hints such as likely procurement links, pagination presence, table/container patterns, and page title. This is related to the procurement-adapter ecosystem, but its primary purpose is adapter reconnaissance and scaffolding assistance, not procurement discovery or reporting. Because the code’s actual behavior is materially narrower and different from the declared primary purpose, this is a clear description-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding

The code is aligned with part of the declared purpose: it searches a Chinese procurement-related platform using configurable keywords and collects dated listings. However, the declared description presents a broader engine with multiple platform coverage, status tracking, and deadline-aware reporting. This specific code chunk only implements a single-platform scraper/adapter for Hebei search results and simple extraction of items. That makes the behavior materially narrower than the declared purpose. There is no evidence of unrelated or dangerous extra capabilities, but there is a description-behavior mismatch because important claimed capabilities are not represented in the supplied code chunk.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The code is broadly consistent with a procurement discovery/scanning component, but it does not fully match the declared description. It only covers one adapter ('national') for a single platform, not a multi-platform engine by itself. Its behavior is limited to scraping listings from six sub-sources within that platform, filtering by date, and optionally searching user queries for one source. There is no evidence in this chunk of bid status tracking, deadline-aware reporting, or a general plugin model. The mismatch is material because the declared purpose describes a broader system capability set than this code actually performs.

Content

No source excerpt is available for this finding.

Self-Modification

High
Category
Rogue Agent
Confidence
85% confidence
Finding

Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Content

Scanner excerpt · bin/bidding-hunter.js (reported line 228)May include surrounding context.

js
// === init ===
program.command('init')
  .description('Create initial config file')
  .option('-f, --force', 'Overwrite existing config')
  .action(async (opts) => {
    const configPath = path.resolve(
      (program.opts().config || '~/.bidding-hunter/config.yaml').replace(/^~/, process.env.HOME || '/home/$USER')

Known Vulnerable Dependency: brace-expansion==1.1.16 — 2 advisory(ies): CVE-2026-14257 (brace-expansion: DoS via unbounded expansion length causing an out-of-memory pro); CVE-2026-69152 (brace-expansion: DoS via unbounded intermediate arrays, bypassing the CVE-2026-1)

High
Category
Supply Chain
Confidence
91% confidence
Finding

The lockfile includes brace-expansion 1.1.16, which is flagged for denial-of-service conditions caused by unbounded expansion behavior. In this project it is only present as a transitive dev dependency under eslint/minimatch, so exploitation would mainly matter during developer-side tooling runs rather than the production bid-discovery workflow, but it is still a real supply-chain weakness if untrusted patterns are processed.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: fast-uri==3.1.4 — 5 advisory(ies): CVE-2026-75931 (fast-uri vulnerable to host confusion via skipped IDN canonicalization on scheme); CVE-2026-18446 (fast-uri vulnerable to host confusion via backslash authority introducer); CVE-2026-75975 (fast-uri vulnerable to server-side request forgery via malformed IPv6 normalizat) +2 more

High
Category
Supply Chain
Confidence
88% confidence
Finding

The lockfile includes fast-uri 3.1.4 via ajv, and the cited advisories describe URI parsing ambiguities that can lead to host confusion or SSRF in applications that make security decisions from parsed URLs. This package-lock.json alone does not prove the application parses attacker-controlled URLs for outbound requests, so the issue is context-dependent, but in a scraper with extensible plugins and configurable monitoring targets, malformed URL handling could become relevant and should be treated as a real risk.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: js-yaml==4.3.0 — 2 advisory(ies): CVE-2026-84375 (js-yaml: maxTotalMergeKeys does not limit CPU use for empty merge sources); GHSA-5p4m-2wfm-xmqj (JS-YAML: Quadratic CPU consumption in !!omap resolution (3.x and 4.x) — CVE-2026)

High
Category
Supply Chain
Confidence
93% confidence
Finding

The lockfile includes js-yaml 4.3.0, which is associated with CPU-exhaustion parsing issues. Given this skill explicitly uses YAML and allows user configuration, a maliciously crafted configuration file could plausibly trigger excessive CPU consumption and disrupt scans or automation, making this more relevant than a purely unused transitive dependency.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding

npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
90% confidence
Finding

The contribution instructions tell users to run npx playwright install chromium without pinning a specific Playwright version. Using npx this way can fetch and execute whatever package version is current at install time, which creates a supply-chain risk if a bad release, typo-squat, or compromised package is served. In a contributor setup document, this is more dangerous because readers are explicitly encouraged to execute the command locally.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

Line L94 states 'Chinese comments for platform-specific logic,' which imposes a specific language requirement in project instructions. This is a natural-language locale policy issue because it forces one language without user opt-in or a documented regional/compliance justification.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · DESIGN.md (reported line 390)May include surrounding context.

md
6. Ingest into database:
   ├── Skip existing URLs
   ├── Assign new aliases
   └── Create history entries
   
7. Fetch details (for tracked entries without deadlines)
   ├── Parallel with concurrency limit

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The extension guide explicitly introduces an exploration mode that accepts an arbitrary URL and has the agent identify selectors, pagination, and extraction logic. That expands the tool from a fixed procurement monitor into a general-purpose website exploration and scraping assistant, increasing abuse potential and weakening the stated scope boundary.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · DESIGN.md (reported line 519)May include surrounding context.

text
   The agent will navigate the site, identify listing pages, pagination, and extraction selectors.

2. **Create adapter from template**:
   ```bash
   bidding-hunter create-adapter --name "my-platform"

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The entire skill documentation is presented only in Chinese, including installation, usage, and safety information, with no indication that users may choose another language or that the tool is intentionally limited to a Chinese-speaking audience. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
94% confidence
Finding

The README instructs users to run npx playwright install chromium, which can cause npx to fetch and execute an unpinned package version at install time. If the upstream package is compromised or a breaking release is published, users could execute unintended code on their machines during setup.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill describes networked scraping, browser automation, filesystem access, and use of environment-backed webhooks, but it does not declare an explicit permission or allowed-tools scope. That weakens containment and review because an agent may execute broader capabilities than users expect, especially when the document also contains strong tool-discipline instructions that normalize code and network use.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · SKILL.md (reported line 44)May include surrounding context.

md
### Rules (ZERO EXCEPTIONS)

1. **Use tools, don't replace them.** A bidding-hunter task is a tool-execution task, NOT a coding task.
2. **No ad-hoc scripts.** Do NOT write `scrape_*.js`, `fetch_*.js`, `test_*.js`, inline `node -e "..."`, `curl`, `web_fetch`, or any one-off scraping code.
3. **No browser shortcuts.** Do NOT use Playwright/Selenium directly unless you are inside a platform adapter's `scan()` method. All browser orchestration is handled by the scanner.
4. **No manual dedup/matching.** Do NOT copy-paste from the matcher or database modules. Use `bidding-hunter scan` → `bidding-hunter report`.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The document claims 'No privacy leakage' and 'all data stays local' while also supporting outbound Feishu and Slack webhooks. That contradiction can cause users to enable notifications without understanding that bid data, keywords, or metadata may be transmitted to third-party services, undermining informed consent and safe deployment decisions.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · SKILL.md (reported line 202)May include surrounding context.

text
   This navigates to the site and captures structural hints.

2. **Create adapter template**:
   ```bash
   bidding-hunter create-adapter --name new-site

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill provides concrete webhook configuration examples but does not warn that scan results and related metadata may leave the local system. For procurement monitoring, this may expose commercially sensitive interests, internal keywords, or opportunity-tracking data to third-party messaging platforms and anyone with access to those channels.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding

The troubleshooting step uses npx playwright install chromium without pinning a version, which can pull whatever package version is current at execution time. This creates supply-chain and reproducibility risk: behavior can change unexpectedly, and a compromised or malicious upstream release would be executed in the user's environment.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.