Back to skill

Security audit

goverment bidding fetcher

Security checks for vulnerabilities and agentic risk

Overview

The skill has a legitimate procurement-scraping purpose, but it handles live account credentials in ways that can expose them.

Review before installing. Use this only in an environment where you are comfortable exposing procurement credentials to the configured network path, avoid passing secrets on the command line, prefer temporary environment variables or a secret store, keep any .env file outside shared/project directories with owner-only permissions, and rotate credentials after use. Treat reports generated from HTTP sources as potentially modifiable in transit.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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)

T09 · Insecure Skill Coding Practices

Error
Location
govb_fetcher/fetcher.py:32
Finding
Authentication Credentials Transmitted over Plaintext HTTP## Vulnerability Details **File Location**: `govb_fetcher/fetcher.py:32-64`, `govb_fetcher/fetcher.py:100-104` **Vulnerability Type**: Cleartext transmission of sensitive authentication data **Risk Level**: High ### Vulnerable Code ```python BASE_URL = 'http://zbcg-bjzc.zhongcy.com/gt-jy-toubiao/api' DETAIL_BASE = 'http://zbcg-bjzc.zhongcy.com/bjczj-jy-toubiao/index.html' HNZC_LIST_URL = 'http://www.ccgp-hunan.gov.cn/mvc/getNoticeList4Web.do' HNZC_DETAIL_URL = 'http://www.ccgp-hunan.gov.cn/mvc/viewNoticeContent.do' HNZC_PAGE_URL = 'http://www.ccgp-hunan.gov.cn/page/notice/notice.jsp' def _build_session() -> requests.Session: session = requests.Session() proxies = get_proxies() if proxies: session.proxies.update(proxies) session.cookies.update({ 'YGCG_TBSESSION': get_bjzc_tbsession(), 'JSESSIONID': get_bjzc_jsessionid(), 'jcloud_alb_route': get_bjzc_alb_route(), }) session.headers.update({ 'Accept': 'application/json, text/plain, */*', 'Accept-Encoding': 'gzip, deflate', 'Accept-Language': 'zh-CN,zh;q=0.9', 'Authorization': f'Bearer {get_bjzc_bearer_token()}', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Content-Type': 'application/x-www-form-urlencoded', 'Origin': 'http://zbcg-bjzc.zhongcy.com', 'Pragma': 'no-cache', 'Referer': 'http://zbcg-bjzc.zhongcy.com/bjczj-jy-toubiao/index.html', 'User-Agent': ( 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' 'AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/146.0.0.0 Safari/537.36' ), 'contentType': 'formType', }) return session ``` ```python def _fetch_bjzc_page(session: requests.Session, page: int, rows: int = 100) -> dict: url = f'{BASE_URL}/cggg/gonggao/queryZBGongGaoList.do' data = { 'ggName' ...[truncated 2173 chars]
Remediation
## Remediation Suggestions 1. Replace all authenticated Beijing API and page endpoints with verified `https://` endpoints. 2. Confirm that the upstream service presents a valid certificate for the expected hostname and retain certificate verification. 3. Explicitly reject redirects from HTTPS to HTTP before credentials can be forwarded. 4. Avoid placing the authorization header on a broadly reusable session if it could contact another origin. Add credentials only to requests whose scheme and hostname exactly match an allowlist. 5. If the upstream service genuinely offers no HTTPS support, do not send reusable credentials directly over the public network. Require an authenticated, trusted tunnel or a secure gateway under the user's control and display a clear security warning. 6. Rotate the Bearer token and session cookies after deploying the fix, because previously transmitted credentials may have been observed.

T09 · Insecure Skill Coding Practices

Warning
Location
govb_fetcher/config.py:143
Finding
Credential Files Created without Enforced Restrictive Permissions## Vulnerability Details **File Location**: `govb_fetcher/config.py:143-167` **Vulnerability Type**: Insecure plaintext credential storage **Risk Level**: Medium ### Vulnerable Code ```python def _save_to_env_unsafe(updates: dict) -> Path: env_path = get_env_path() if not env_path.exists(): example = Path(__file__).parent.parent / '.env.example' env_path.parent.mkdir(parents=True, exist_ok=True) if example.exists(): env_path.write_text(example.read_text(encoding='utf-8'), encoding='utf-8') else: env_path.write_text('', encoding='utf-8') lines = env_path.read_text(encoding='utf-8').splitlines(keepends=True) updated_keys = set() for i, line in enumerate(lines): for k, v in updates.items(): if re.match(rf'^\s*{re.escape(k)}\s*=', line): lines[i] = f'{k}={v}\n' updated_keys.add(k) for k, v in updates.items(): if k not in updated_keys: lines.append(f'{k}={v}\n') env_path.write_text(''.join(lines), encoding='utf-8') ``` ### Technical Analysis The Skill stores the Beijing Bearer token and session cookies as plaintext `key=value` entries. When it creates the `.env` file, it uses `Path.write_text()` without explicitly selecting a restrictive file mode. Consequently, the resulting permissions depend on the process umask. Under a permissive umask, other local users may be able to read the file. The parent configuration directory is similarly created without enforcing owner-only access. Existing files are rewritten without checking or correcting their permission mode. The same configuration mechanism can also contain a proxy URL with embedded credentials. ### Attack Path 1. A user runs `--set-cookie` on a system with a permissive umask or with an existing broadly readable `.env` file. 2. The Skill creates or updates the file without enforcing mod ...[truncated 850 chars]
Remediation
## Remediation Suggestions 1. Prefer an operating-system credential store or dedicated secret manager instead of plaintext `.env` persistence. 2. If file storage is retained, create the parent directory with mode `0700`. 3. Create new credential files atomically with mode `0600`, rather than relying on the process umask. 4. Validate and correct the mode of existing files before reading or writing credentials. 5. Write updates to a securely created temporary file in the same directory, flush and atomically replace the destination to avoid partial writes. 6. Reject symlinks and verify that the destination is a regular file owned by the current user. 7. Prefer the user-specific configuration directory over a current-working-directory `.env` for secrets. 8. Rotate credentials that may previously have been stored in a broadly readable file.

T09 · Insecure Skill Coding Practices

Warning
Location
govb_fetcher/fetcher.py:823
Finding
Secrets Exposed through Command-Line Arguments and Console Output## Vulnerability Details **File Location**: `govb_fetcher/fetcher.py:823-842` **Vulnerability Type**: Sensitive information exposure through process arguments and logs **Risk Level**: Medium ### Vulnerable Code ```python env_path = save_to_env(updates) print(f'[ok] [{source}] credentials written to: {env_path}') for k, v in updates.items(): print(f' {k} = {v[:12]}...' if len(v) > 12 else f' {k} = {v}') def main() -> None: parser = argparse.ArgumentParser( prog='govb-fetcher', description='Government procurement opportunity fetcher', ) parser.add_argument('--set-cookie', action='store_true', help='Update credentials in .env without fetching') parser.add_argument('--source', default='bjzc', help='Credential source identifier') parser.add_argument('--bearer', default='', help='Bearer token in "Bearer xxx" or "xxx" format') parser.add_argument('--session', default='', help='Cookie string such as "YGCG_TBSESSION=xxx; JSESSIONID=xxx; ..."') ``` The original source uses equivalent Chinese user-facing messages; the security-relevant operations are the argument definitions and value-printing behavior shown above. ### Technical Analysis The documented credential update flow passes the Bearer token and complete Cookie string as command-line arguments. Depending on the operating system and environment, process arguments may be observable through process inspection tools, process accounting, telemetry agents, job logs, shell history, or terminal recording. After persistence, the code prints the first 12 characters of values longer than 12 characters and prints shorter values in full. Partial token disclosure can provide useful identifying or secret material, while complete display of short values directly discloses them to logs and observers. ### Attack P ...[truncated 1135 chars]
Remediation
## Remediation Suggestions 1. Do not accept secrets directly as ordinary command-line values. 2. Read them from an interactive hidden prompt using `getpass`, protected standard input, a file descriptor, or an operating-system secret store. 3. If file-based import is supported, require owner-only permissions and avoid echoing file contents. 4. Never print full or partial secret values. Report only the updated variable names and success status. 5. Update the documentation so examples do not encourage embedding credentials in shell commands. 6. Advise affected users to remove secrets from shell history and execution logs, then rotate the exposed credentials.

T08 · Insecure Dependencies

Note
Location
pyproject.toml:6
Finding
Unpinned Runtime Dependencies Permit Unreviewed Package Resolution## Vulnerability Details **File Location**: `pyproject.toml:6-10` **Vulnerability Type**: Unconstrained third-party dependency resolution **Risk Level**: Low ### Vulnerable Code ```toml dependencies = [ "requests", "openpyxl", "python-dotenv", ] ``` ### Technical Analysis All runtime dependencies are declared without exact versions, upper bounds, lock-file references, or integrity hashes. Each installation can therefore resolve to whichever compatible release is available from the configured package index at that time. The package names are conventional and the audit found no evidence of typosquatting, dependency confusion, or a currently malicious package. Nevertheless, unconstrained resolution makes builds non-reproducible and permits future compromised, incompatible, or unexpectedly changed releases to enter the execution environment without project-level review. `python-dotenv` also appears unused by the inspected source, increasing the dependency surface without an evident functional requirement. ### Attack Path 1. A dependency account, release pipeline, or configured package index is compromised, or a vulnerable release is published. 2. A user installs the Skill after that release becomes available. 3. The package resolver selects the unreviewed version because no reviewed version or hash is enforced. 4. Malicious installation or runtime code executes with the privileges of the user running or installing the Skill. 5. That code can access files and environment data available to the process, potentially including the procurement credentials loaded by the Skill. ### Impact Assessment Successful supply-chain exploitation executes within the Skill's Python environment with the operating-system privileges of the installing or invoking user. It could read user-accessible files, access environment variables, alter generated reports, or communicate over the network. The practical likelihood is ...[truncated 196 chars]
Remediation
## Remediation Suggestions 1. Generate and commit a lock file containing reviewed, reproducible versions. 2. Use integrity hashes for deployment installations where supported. 3. Define controlled version constraints and test updates before release. 4. Enable automated vulnerability and package-provenance monitoring. 5. Review transitive dependencies as part of the release process. 6. Remove `python-dotenv` if it remains unused. 7. Install packages only from an explicitly trusted package index.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (26)

Credential Access

High
Category
Privilege Escalation
Content
- 后续新增数据源:FETCHER_CCGP_xxx / FETCHER_BJGP_xxx / ...

优先级(高→低):
  1. 当前运行目录的 .env
  2. ~/.config/govb-fetcher/.env
  3. 硬编码默认值(仅关键词等非敏感配置)
"""
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
- 后续新增数据源:FETCHER_CCGP_xxx / FETCHER_BJGP_xxx / ...

优先级(高→低):
  1. 当前运行目录的 .env
  2. ~/.config/govb-fetcher/.env
  3. 硬编码默认值(仅关键词等非敏感配置)
"""
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
- 后续新增数据源:FETCHER_CCGP_xxx / FETCHER_BJGP_xxx / ...

优先级(高→低):
  1. 当前运行目录的 .env
  2. ~/.config/govb-fetcher/.env
  3. 硬编码默认值(仅关键词等非敏感配置)
"""
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
- 后续新增数据源:FETCHER_CCGP_xxx / FETCHER_BJGP_xxx / ...

优先级(高→低):
  1. 当前运行目录的 .env
  2. ~/.config/govb-fetcher/.env
  3. 硬编码默认值(仅关键词等非敏感配置)
"""
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
- 后续新增数据源:FETCHER_CCGP_xxx / FETCHER_BJGP_xxx / ...

优先级(高→低):
  1. 当前运行目录的 .env
  2. ~/.config/govb-fetcher/.env
  3. 硬编码默认值(仅关键词等非敏感配置)
"""
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
- 后续新增数据源:FETCHER_CCGP_xxx / FETCHER_BJGP_xxx / ...

优先级(高→低):
  1. 当前运行目录的 .env
  2. ~/.config/govb-fetcher/.env
  3. 硬编码默认值(仅关键词等非敏感配置)
"""
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
cfg = dict(DEFAULTS)

    # 全局配置(低优先级)
    global_env = Path.home() / '.config' / 'govb-fetcher' / '.env'
    cfg.update(_load_env_file(global_env))

    # 局部配置(高优先级)
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
cfg = dict(DEFAULTS)

    # 全局配置(低优先级)
    global_env = Path.home() / '.config' / 'govb-fetcher' / '.env'
    cfg.update(_load_env_file(global_env))

    # 局部配置(高优先级)
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
cfg = dict(DEFAULTS)

    # 全局配置(低优先级)
    global_env = Path.home() / '.config' / 'govb-fetcher' / '.env'
    cfg.update(_load_env_file(global_env))

    # 局部配置(高优先级)
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
cfg.update(_load_env_file(local_env))

    # 环境变量最高优先级
    for k in list(cfg.keys()) + list(os.environ.keys()):
        if k.startswith('FETCHER_') and k in os.environ:
            cfg[k] = os.environ[k]
Confidence
60% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Credential Access

High
Category
Privilege Escalation
Content
local = Path.cwd() / '.env'
    if local.exists():
        return local
    return Path.home() / '.config' / 'govb-fetcher' / '.env'


def save_to_env(updates: dict) -> Path:
Confidence
78% confidence
Finding
The save_to_env path selection prefers a .env in the current working directory when one exists, and then writes credential updates into that file. In a tool that handles bearer tokens and session cookies, running from an untrusted or shared directory could cause sensitive credentials to be persisted into an attacker-visible location, creating accidental credential disclosure.

Credential Access

High
Category
Privilege Escalation
Content
# --set-cookie 子命令
def cmd_set_cookie(source: str, bearer: str, session_str: str) -> None:
    """解析并写入指定数据源的凭证到 .env 文件。"""
    source = source.lower().strip()
    if source not in SOURCE_COOKIE_MAP:
        known = ', '.join(SOURCE_COOKIE_MAP.keys())
Confidence
93% confidence
Finding
This finding points to the credential-management path that explicitly writes bearer tokens and cookies into a .env file. Although the code is not exfiltrating secrets, it performs credential collection and storage in a plain-text local file, which is a real credential-handling risk because compromise of the workstation, repository, or backups exposes reusable authentication material.

Credential Access

High
Category
Privilege Escalation
Content
# 凭证更新子命令
    parser.add_argument('--set-cookie', action='store_true',
                        help='更新 .env 中的凭证信息(不执行抓取)')
    parser.add_argument('--source', default='bjzc',
                        help=f'数据源标识,与 --set-cookie 配合使用,可选: {", ".join(SOURCE_COOKIE_MAP.keys())}(默认: bjzc)')
    parser.add_argument('--bearer', default='',
Confidence
91% confidence
Finding
The CLI exposes a dedicated --set-cookie workflow that normalizes credential ingestion as part of routine tool operation. In the context of a procurement scraper, this increases operational risk by encouraging users to paste live session tokens into command lines and store them locally, where they may be visible to shell history, process listings, or other local observers.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill advertises capabilities that imply environment access, file reads/writes, and network use, but it does not declare any explicit tool scope or permissions boundary. In an agent ecosystem, this weakens least-privilege controls and makes it harder for users or the runtime to understand and constrain what the skill can access, especially since it also handles credentials and writes output files.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill instructs users to pass Bearer tokens and session cookies and also supports storing them via environment variables, but it provides no warning about secure handling, persistence, rotation, or leakage risks. In this context, those credentials grant authenticated access to procurement platforms, so accidental exposure through shell history, logs, config files, or shared workspaces could lead to unauthorized account use.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The trigger phrases are broad enough to match ordinary conversation about government procurement, which can cause the skill to activate unexpectedly. While this is not direct code execution risk, unintended activation can still expose credentials, initiate scraping, or create files/network requests when the user did not clearly intend to run this specific skill.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The module docstring and all user-facing descriptive text are written only in Chinese, which constitutes a language/locale constraint in the skill's natural-language surface. There is no indication that users may choose another language or that the tool is intentionally restricted to a Chinese-speaking or region-specific audience.

Context-Inappropriate Capability

Medium
Confidence
98% confidence
Finding
The code silently captures refreshed YGCG_TBSESSION values from server responses and writes the newest session cookie back to .env during scraping. Automatic secret persistence without clear user awareness broadens credential exposure and can retain valid session material longer than needed.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The skill includes a CLI feature that accepts bearer tokens and cookie strings, parses them, and persists them into a local .env file. For a scraper/report generator, storing live authentication material on disk is unnecessary for many use cases and increases the chance of credential theft, unintended reuse, or accidental inclusion in backups, logs, or version control.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
After writing credentials, the program only reports success and even prints truncated secret values, but gives no warning about the security implications of storing authentication tokens locally. This can mislead users into treating sensitive session material as harmless configuration and increases the risk of leakage through terminal history, screenshots, shared systems, or insecure file handling.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
The skill description is written entirely in Chinese and does not indicate that language choice is optional or that the skill is intended only for Chinese-speaking users. Under the policy, forcing a specific language without user opt-in can be a natural-language policy violation unless the locale restriction is clearly documented and justified.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
Natural-language strings throughout the file, including the module description and CLI help/output, are fixed in Chinese with no opt-in or locale selection mechanism. This can violate language or locale policy when organizational standards require user choice rather than a forced language.

Natural-Language Policy Violations

Low
Confidence
89% confidence
Finding
The manifest description is entirely in Chinese ("政府采购商机自动抓取工具"), which indicates a language-specific presentation without offering any user language choice or opt-in. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy concern.

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
91% confidence
Finding
The manifest declares requests without a version constraint, so builds may resolve to vulnerable or unexpected releases depending on installer state and time of installation. For a network-scraping tool that likely makes HTTP requests to external sites, dependency drift increases the chance of pulling a version affected by credential leakage, TLS, redirect, or other client-side issues.

Unverifiable Dependency: openpyxl has 2 known advisory(ies) (CVE-2017-5992 (Improper Restriction of XML External Entity Reference in Openpyxl); CVE-2017-5992 (Openpyxl 2.4.1 resolves external entities by default, which allows remote attack)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
87% confidence
Finding
openpyxl is unpinned, so the installed version may vary and could include releases affected by known XML parsing issues such as XXE in older versions. In this skill's context, Excel generation is part of normal operation, which makes unsafe parser behavior less central than the network stack, but dependency uncertainty still creates avoidable supply-chain and parser risk.

Static analysis

No suspicious patterns detected.