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

Content
View full analysis

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
View 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

Content
View full analysis

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
View 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

Content
View full analysis

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
View 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

Content
View full analysis

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
View 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
Confidence
60% confidence
Finding

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

Content

Scanner excerpt · CLAUDE.md (reported line 26)May include surrounding context.

md
- 后续新增数据源:FETCHER_CCGP_xxx / FETCHER_BJGP_xxx / ...

优先级(高→低):
  1. 当前运行目录的 .env
  2. ~/.config/govb-fetcher/.env
  3. 硬编码默认值(仅关键词等非敏感配置)
"""

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 · govb_fetcher/config.py (reported line 10)May include surrounding context.

python
- 后续新增数据源:FETCHER_CCGP_xxx / FETCHER_BJGP_xxx / ...

优先级(高→低):
  1. 当前运行目录的 .env
  2. ~/.config/govb-fetcher/.env
  3. 硬编码默认值(仅关键词等非敏感配置)
"""

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 · govb_fetcher/config.py (reported line 11)May include surrounding context.

python
- 后续新增数据源:FETCHER_CCGP_xxx / FETCHER_BJGP_xxx / ...

优先级(高→低):
  1. 当前运行目录的 .env
  2. ~/.config/govb-fetcher/.env
  3. 硬编码默认值(仅关键词等非敏感配置)
"""

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 · govb_fetcher/config.py (reported line 28)May include surrounding context.

python
- 后续新增数据源:FETCHER_CCGP_xxx / FETCHER_BJGP_xxx / ...

优先级(高→低):
  1. 当前运行目录的 .env
  2. ~/.config/govb-fetcher/.env
  3. 硬编码默认值(仅关键词等非敏感配置)
"""

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 · govb_fetcher/config.py (reported line 124)May include surrounding context.

python
- 后续新增数据源:FETCHER_CCGP_xxx / FETCHER_BJGP_xxx / ...

优先级(高→低):
  1. 当前运行目录的 .env
  2. ~/.config/govb-fetcher/.env
  3. 硬编码默认值(仅关键词等非敏感配置)
"""

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 · govb_fetcher/config.py (reported line 127)May include surrounding context.

python
- 后续新增数据源:FETCHER_CCGP_xxx / FETCHER_BJGP_xxx / ...

优先级(高→低):
  1. 当前运行目录的 .env
  2. ~/.config/govb-fetcher/.env
  3. 硬编码默认值(仅关键词等非敏感配置)
"""

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 · govb_fetcher/config.py (reported line 57)May include surrounding context.

python
cfg = dict(DEFAULTS)

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

    # 局部配置(高优先级)

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 · govb_fetcher/config.py (reported line 61)May include surrounding context.

python
cfg = dict(DEFAULTS)

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

    # 局部配置(高优先级)

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 · govb_fetcher/config.py (reported line 128)May include surrounding context.

python
cfg = dict(DEFAULTS)

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

    # 局部配置(高优先级)

Env Variable Harvesting

High
Category
Data Exfiltration
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.

Content

Scanner excerpt · govb_fetcher/config.py (reported line 65)May include surrounding context.

python
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]

Credential Access

High
Category
Privilege Escalation
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.

Content

Scanner excerpt · govb_fetcher/config.py (reported line 131)May include surrounding context.

python
local = Path.cwd() / '.env'
    if local.exists():
        return local
    return Path.home() / '.config' / 'govb-fetcher' / '.env'


def save_to_env(updates: dict) -> Path:

Credential Access

High
Category
Privilege Escalation
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.

Content

Scanner excerpt · govb_fetcher/fetcher.py (reported line 786)May include surrounding context.

python
# --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())

Credential Access

High
Category
Privilege Escalation
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.

Content

Scanner excerpt · govb_fetcher/fetcher.py (reported line 835)May include surrounding context.

python
# 凭证更新子命令
    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='',

Undeclared Tool Scope

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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.