Back to skill

Security audit

jenkins-fix

Security checks for vulnerabilities and agentic risk

Overview

This Jenkins helper appears purpose-built for CI/CD chat operations, but it uses high-impact Jenkins credentials and build authority with unsafe defaults and weak safeguards.

Install only in a controlled Jenkins/DingTalk environment after changing the Jenkins URL to HTTPS, using a least-privilege service token, adding job allowlists and confirmation before builds, resolving ambiguous job names safely, and redacting or disabling raw console-log output to chat.

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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/jenkins_handler.py:15
Finding
Jenkins Credentials Are Transmitted over Plaintext HTTP## Vulnerability Details **File Location**: `scripts/jenkins_handler.py:15-18, 33-45, 61-69` **Vulnerability Type**: Cleartext transmission of authentication credentials **Risk Level**: High ### Vulnerable Code ```python JENKINS_URL = os.getenv("JENKINS_URL", "http://jks.huimei-inc.com") USERNAME = os.getenv("JENKINS_USERNAME", "jiaofu") API_TOKEN = os.getenv("JENKINS_API_TOKEN", "") PASSWORD = os.getenv("JENKINS_PASSWORD", "") ``` ```python if try_api_token_first and API_TOKEN: try: test_url = f"{JENKINS_URL}/api/json" response = session.get( test_url, auth=(USERNAME, API_TOKEN), timeout=10 ) if response.status_code == 200: return (USERNAME, API_TOKEN) except Exception: pass if PASSWORD: return (USERNAME, PASSWORD) ``` ```python headers = { "Authorization": f"Basic {base64.b64encode(f'{auth[0]}:{auth[1]}'.encode()).decode()}" } ``` ### Technical Analysis The default Jenkins endpoint uses unencrypted HTTP. The `requests` authentication tuple causes the username and API token or password to be sent using HTTP Basic authentication. Basic authentication only Base64-encodes the credentials; it does not provide encryption. The Base64 operation identified by the pre-scan is used to construct a conventional HTTP Basic Authorization header. There is no evidence that this encoded value is printed or used as a covert output channel. Nevertheless, sending that header over HTTP exposes the underlying credential to anyone capable of observing or manipulating network traffic. The insecure HTTP endpoint is also documented in `SKILL.md`, making insecure deployment the default rather than an exceptional configuration. ### Attack Path 1. A user configures or accepts the default `http://jks.huimei-inc.com` endpoint. 2. The Skill sends a Jenkins API request using a username and API token or pas ...[truncated 1274 chars]
Remediation
## Remediation Suggestions 1. Change the default endpoint to an HTTPS URL with a valid certificate. 2. Reject any configured `JENKINS_URL` that does not use the `https` scheme. 3. Keep TLS certificate verification enabled and do not introduce `verify=False`. 4. Remove all plaintext HTTP Jenkins examples from `SKILL.md`. 5. Use a dedicated, revocable Jenkins API token instead of an account password. 6. Assign the Jenkins service account only the job-read, build-trigger, build-status, and artifact-read permissions required for approved jobs. 7. Restrict the account from administrative actions, credential management, script-console access, and unrelated deployment jobs. 8. Rotate any credential that may already have traversed the plaintext endpoint. 9. Consider pinning the expected Jenkins hostname and applying outbound network controls so credentials cannot be sent to an arbitrary environment-configured host.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/jenkins_handler.py:180
Finding
Unredacted Jenkins Failure Logs May Be Disclosed through DingTalk Output## Vulnerability Details **File Location**: `scripts/jenkins_handler.py:180-190, 316-320, 467-471`; `scripts/dingtalk_jenkins.py:82-91` **Vulnerability Type**: Sensitive information exposure through raw build logs **Risk Level**: Medium ### Vulnerable Code ```python def get_build_console_output(job_name, build_number): try: init_auth() encoded_name = requests.utils.quote(job_name, safe='') url = f"{JENKINS_URL}/job/{encoded_name}/{build_number}/consoleText" response = session.get(url, auth=AUTH, timeout=30) response.raise_for_status() return response.text[-5000:] except Exception as e: return f"Log retrieval failed: {str(e)}" ``` ```python if console_output: lines.append("\nFailure log, final 2000 characters:") lines.append("```") lines.append(console_output[-2000:]) lines.append("```") ``` ```python result = subprocess.run( ["python3", JENKINS_SCRIPT, cmd_line], capture_output=True, text=True, timeout=400 ) if result.returncode == 0: print(result.stdout) else: print(f"Build failed: {result.stderr}") ``` ### Technical Analysis When a build fails, the handler retrieves up to 5,000 characters from the Jenkins console log and includes the final 2,000 characters verbatim in its output. The DingTalk-facing wrapper then relays the handler output without sanitization. Jenkins logs frequently include environment details, internal hostnames, source paths, deployment commands, stack traces, repository URLs, artifact locations, and credentials accidentally emitted by build tools. Truncating the log does not constitute redaction and does not ensure that sensitive information is excluded. This behavior is broader than the minimum information necessary to report whether a build succeeded or failed. It also transfers information from Jenkins's access-controlled interface into a chat contex ...[truncated 1615 chars]
Remediation
## Remediation Suggestions 1. Do not include raw Jenkins console output in chat responses by default. 2. Return a concise failure status, build number, and permission-controlled Jenkins URL instead. 3. If excerpts are operationally necessary, make them an explicit opt-in action restricted to authorized users. 4. Redact common secret patterns before output, including: - Authorization headers and Basic/Bearer credentials. - Password, token, secret, and API-key assignments. - URLs containing embedded credentials or sensitive query parameters. - Private keys and cloud-provider access keys. 5. Apply output-length limits only after redaction; truncation alone is not sufficient. 6. Verify that the requesting DingTalk identity is authorized to view the selected Jenkins job and build. 7. Ensure chat membership and retention controls are at least as restrictive as the corresponding Jenkins access policy. 8. Configure Jenkins jobs and build tools to mask secrets at their source.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/jenkins_handler.py:417
Finding
Ambiguous Substring Matching Can Trigger an Unintended Jenkins Job## Vulnerability Details **File Location**: `scripts/jenkins_handler.py:417-427` **Vulnerability Type**: Unsafe target selection for a state-changing operation **Risk Level**: Medium ### Vulnerable Code ```python matched = None for job in jobs: if project.lower() in job.get("name", "").lower(): matched = job.get("name") break if not matched: for job in jobs: if project.lower() == job.get("name", "").lower(): matched = job.get("name") break ``` ```python result = trigger_build(matched, branch=branch) ``` ### Technical Analysis The code performs substring matching before exact matching. An exact project name is therefore not guaranteed to select the exact job if another job containing that text appears earlier in the Jenkins API response. When multiple jobs contain the supplied text, the first server-ordered match is selected without displaying the alternatives or requiring user confirmation. The selected target is then passed directly to `trigger_build`, which performs a state-changing Jenkins operation. This is a logic and authorization-safety issue rather than shell command injection. Job names are URL-encoded, and the subprocess wrapper uses an argument array without `shell=True`, so the reviewed code does not establish a shell-injection path. ### Attack Path 1. Jenkins contains multiple jobs whose names share a common substring, such as a testing job and a production deployment job. 2. A user supplies that substring or an exact name that is also contained in an earlier API result. 3. The first substring match is accepted immediately. 4. The code does not detect ambiguity or ask the user to confirm the resolved job. 5. `trigger_build` starts the incorrectly selected job. 6. Any side effects configured for that job occur under the Jenkins account's permissions. The likelihood and impact depend on Jenkins job naming and ordering, but t ...[truncated 769 chars]
Remediation
## Remediation Suggestions 1. Perform case-insensitive exact matching before any fuzzy matching. 2. Collect all substring matches rather than accepting the first result. 3. Reject ambiguous requests and return the complete set of candidate job names. 4. Require explicit confirmation before triggering a fuzzy-resolved job. 5. Use stable Jenkins identifiers where available rather than display-name substrings. 6. Introduce an allowlist of jobs that the Skill is permitted to trigger. 7. Use a Jenkins service account restricted to those approved jobs. 8. Log the requesting identity, supplied project text, resolved job name, branch, and confirmation event for auditability. 9. For deployment jobs, require additional authorization or approval before invocation.
Vulnerability Patterns
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (7)

Vague Triggers

Medium
Confidence
94% confidence
Finding
The trigger keywords include very broad terms such as “build”, “部署”, “branch”, and “tag”, which can appear in ordinary conversation outside a clearly intended Jenkins workflow. This can cause the skill to activate unexpectedly and perform sensitive CI/CD actions like listing jobs, triggering builds, or exposing build outputs in response to ambiguous user input.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The module docstring states the skill is specifically for DingTalk group Jenkins commands, and all user-facing prompts and command formats are hard-coded in Chinese. This imposes a language constraint without opt-in or an explicit justification that the skill is region- or locale-specific under organizational policy.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
return
    
    if cmd["action"] == "list":
        result = subprocess.run(
            ["python3", JENKINS_SCRIPT, "Jenkins 项目列表"],
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
print(f"🔄 正在处理 Jenkins 构建请求...\n")
        
        result = subprocess.run(
            ["python3", JENKINS_SCRIPT, cmd_line],
            capture_output=True,
            text=True,
Confidence
84% confidence
Finding
Although subprocess.run is invoked safely with an argument list and no shell, this code forwards user-controlled project and branch data into another script that likely performs privileged Jenkins operations. In the context of a DingTalk-triggered build bridge, this can enable unauthorized or unsafe build execution, and if the downstream handler unsafely interpolates these values, it may also become an injection vector there.

Unsafe Defaults

Medium
Category
Tool Misuse
Content
return headers

# 认证(延迟初始化,避免启动时暴露敏感信息)
AUTH = None
AUTH_HEADER = None

def init_auth():
Confidence
75% confidence
Finding
Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The script will trigger a Jenkins build immediately after resolving a matching job name, without any explicit confirmation or secondary approval step. In an agent or chat-driven context, ambiguous input, prompt injection, or simple user mistakes could cause unauthorized or unintended CI/CD executions, consuming infrastructure or deploying unsafe code paths.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The skill's natural-language interface, help text, and command syntax are presented only in Chinese, effectively forcing a specific language for users. There is no indication that the user can opt into another language or locale, which conflicts with the language-choice policy described in the audit criteria.

Static analysis

No suspicious patterns detected.