Back to skill

Security audit

Reactive Resume

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent Reactive Resume development guide, but its included database reset and deployment examples contain high-impact unsafe patterns that users should review before use.

Install only if you are comfortable reviewing and controlling the setup steps. Do not run scripts/db-reset.py against any database that is not disposable, do not let untrusted values control DATABASE_URL, replace all example secrets and tokens, avoid exposing Browserless or databases publicly, and treat Docker socket, sudo, cron, and restore examples as high-impact operations requiring explicit user approval.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (7)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/db-reset.py:33
Finding
Shell Command Injection Through DATABASE_URL<![CDATA[ ## Vulnerability Details **File Location**: `scripts/db-reset.py`, lines 33–42 and 49–94 **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code ```python def run_command(command, capture=False): """运行 shell 命令""" try: if capture: result = subprocess.run(command, shell=True, capture_output=True, text=True) return result.returncode, result.stdout, result.stderr else: subprocess.run(command, shell=True) return 0, "", "" except Exception as e: print_colored(f"Error: {e}", Colors.RED) return 1, "", str(e) ``` ```python def check_env(): """检查环境变量""" db_url = os.environ.get('DATABASE_URL') if not db_url: if os.path.exists('.env'): with open('.env', 'r') as f: for line in f: if line.startswith('DATABASE_URL='): db_url = line.split('=', 1)[1].strip() break ``` ```python # 执行 cmd = f"psql {db_url} -f /tmp/drop_tables.sql" returncode, stdout, stderr = run_command(cmd, capture=True) ``` ### Technical Analysis The database URL is read from an environment variable or `.env` file, interpolated directly into a command string, and passed to `subprocess.run` with `shell=True`. No shell escaping or argument separation is applied. Consequently, shell metacharacters in `DATABASE_URL`, including `;`, `&&`, command substitutions, pipes, or redirections, are interpreted by the shell rather than being passed only to `psql`. ### Attack Path 1. An attacker gains the ability to influence `DATABASE_URL`, the project `.env` file, or the environment from which the script is launched. 2. The attacker supplies a value containing a valid-looking connection string followed by shell syntax, such as: ```text postgresql://user:pass@localhost/db; attacker-command # ``` 3. A developer or automation process invokes `scripts/db-reset.py`. 4. ...[truncated 688 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove `shell=True` and pass arguments as a list: ```python subprocess.run( ["psql", db_url, "-f", sql_path], check=True, capture_output=True, text=True, ) ``` - Replace the generic string-based `run_command` function with an API that accepts only an argument list. - Parse `DATABASE_URL` with a PostgreSQL URI parser and reject malformed or unsupported schemes. - Do not attempt to solve this only through shell quoting; avoiding shell interpretation is the safer control. - Protect `.env` from untrusted modification and apply restrictive filesystem permissions. - Use `check=True` or explicitly inspect every subprocess return code. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/db-reset.py:73
Finding
Predictable Temporary SQL File Enables Symlink and Race Attacks<![CDATA[ ## Vulnerability Details **File Location**: `scripts/db-reset.py`, lines 73–94 **Vulnerability Type**: Insecure temporary-file handling **Risk Level**: Medium ### Vulnerable Code ```python def drop_all_tables(db_url): """删除所有表""" print_colored("\n🗑️ Dropping all tables...", Colors.YELLOW) # 使用 psql 删除所有表 drop_sql = """ DO $$ DECLARE r RECORD; BEGIN FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = 'public') LOOP EXECUTE 'DROP TABLE IF EXISTS public.' || quote_ident(r.tablename) || ' CASCADE'; END LOOP; END $$; """ # 写入临时文件 with open('/tmp/drop_tables.sql', 'w') as f: f.write(drop_sql) # 执行 cmd = f"psql {db_url} -f /tmp/drop_tables.sql" returncode, stdout, stderr = run_command(cmd, capture=True) ``` ### Technical Analysis The script writes destructive SQL to the fixed path `/tmp/drop_tables.sql`. Shared temporary directories are commonly writable by multiple local users. Opening a predictable path with normal write mode does not prevent following a pre-existing symbolic link. There is also a time-of-check/time-of-use interval between writing the file and invoking `psql`. Another local process may replace or modify the file during that interval. The file is not removed after use. ### Attack Path 1. A local attacker predicts that the script will use `/tmp/drop_tables.sql`. 2. The attacker creates that path as a symbolic link to a file writable by the victim, or continuously replaces the path during execution. 3. A more privileged user runs the database reset utility. 4. The script follows the symbolic link when opening the path, potentially overwriting the linked target. 5. Alternatively, the attacker replaces the SQL file after it is written but before `psql` reads it. 6. `psql` then executes attacker-controlled SQL using the database credentials supplied to the script. ### Impact Assessment The filesystem impact is limited ...[truncated 327 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Prefer sending SQL to `psql` over standard input, eliminating the temporary file: ```python subprocess.run( ["psql", db_url], input=drop_sql, text=True, check=True, ) ``` - If a file is required, create it with `tempfile.NamedTemporaryFile` or `tempfile.mkstemp`. - Ensure the file is created atomically with permissions limited to the current user. - Close and delete it in a `finally` block. - Avoid shared, predictable paths and do not reuse a global filename between invocations. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/db-reset.py:63
Finding
Production Database Safeguard Is Bypassable and Connection Credentials Are Printed<![CDATA[ ## Vulnerability Details **File Location**: `scripts/db-reset.py`, lines 63–69 and 137–160 **Vulnerability Type**: Unsafe destructive-operation validation and sensitive information exposure **Risk Level**: High ### Vulnerable Code ```python # 检查是否是开发环境(localhost 或 postgres) if 'localhost' not in db_url and 'postgres' not in db_url: print_colored("⚠️ Warning: DATABASE_URL does not look like a development database.", Colors.YELLOW) print(f" URL: {db_url}") response = input("Continue anyway? (y/N): ") if response.lower() != 'y': return None ``` ```python print_colored("This will DELETE ALL DATA from the database.", Colors.RED) print_colored("This action is IRREVERSIBLE!", Colors.RED) print_colored("=" * 60, Colors.BLUE) # 检查环境 db_url = check_env() if not db_url: print_colored("\n❌ Aborted.", Colors.RED) return False print(f"\nDatabase: {db_url}") # 确认 if not confirm: print_colored("\nType 'DELETE' to confirm: ", Colors.RED, end='') confirmation = input().strip() if confirmation != 'DELETE': print_colored("\n❌ Aborted.", Colors.RED) return False ``` ```python parser.add_argument('--confirm', action='store_true', help='Skip confirmation prompt') ``` ### Technical Analysis The environment check treats any URL containing either `localhost` or `postgres` as development-like. Normal PostgreSQL connection strings generally contain the substring `postgres`, including through the `postgresql://` scheme, a username, a hostname, or a database name. Therefore, many production URLs bypass the warning entirely. The `--confirm` option also skips the typed destructive-action confirmation. Together, these controls allow a production database to be reset noninteractively without a meaningful environment boundary. The script additionally prints the complete database URL. PostgreSQL URLs frequently contain plaintext usernames and passwords, which may consequently appear in terminal history, CI logs, sc ...[truncated 1057 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Parse the connection URI and compare exact normalized fields rather than using substring matching. - Refuse non-local targets by default. Maintain an explicit allowlist of development hosts, ports, and database names. - Require an explicit marker such as `APP_ENV=development` in addition to target validation. - Require the operator to type the exact database hostname and database name before deletion, including in noninteractive workflows. - Replace `--confirm` with a more explicit option such as `--i-understand-this-deletes DATABASE_NAME`, and still reject production targets. - Consider requiring a database-side marker table that identifies the database as disposable. - Redact connection output: ```text postgresql://user:***@host/database ``` - Use a minimally privileged development-only database account. - Create and verify a backup before destructive execution where practical. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/create-template.py:112
Finding
Template Name Path Traversal and Unconditional File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `scripts/create-template.py`, lines 112–160 **Vulnerability Type**: Path traversal and arbitrary file overwrite **Risk Level**: Medium ### Vulnerable Code ```python def create_template(template_name: str, output_path: str): """创建模板目录结构""" # 转换为不同命名格式 snake_name = template_name.lower().replace('-', '_') pascal_name = ''.join(word.capitalize() for word in snake_name.split('_')) display_name = ' '.join(word.capitalize() for word in snake_name.split('_')) # 创建目录 template_dir = os.path.join(output_path, snake_name) os.makedirs(template_dir, exist_ok=True) # 创建 components 子目录 components_dir = os.path.join(template_dir, 'components') os.makedirs(components_dir, exist_ok=True) ``` ```python for filename, content in files.items(): filepath = os.path.join(template_dir, filename) with open(filepath, 'w', encoding='utf-8') as f: f.write(content) print(f"✓ Created: {filepath}") # 创建 README readme_path = os.path.join(template_dir, 'README.md') ... with open(readme_path, 'w', encoding='utf-8') as f: f.write(readme_content) ``` ### Technical Analysis The only transformation applied to `template_name` is lowercasing and replacement of hyphens with underscores. Directory separators, absolute paths, and `..` segments remain valid. `os.path.join()` does not enforce containment under `output_path`. A traversal value can therefore resolve outside the expected template directory. In addition, `exist_ok=True` and write mode (`'w'`) cause existing generated files to be overwritten without confirmation. ### Attack Path 1. An attacker or untrusted automation input controls the template name. 2. A traversal value such as `../../target` is supplied. 3. The script joins that value with the default `public/templates` path. 4. Path normalization by the operating system places `template_dir` outside the intended output root. 5. The script creates dire ...[truncated 587 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Restrict template names to a safe basename: ```python if not re.fullmatch(r"[a-z0-9][a-z0-9_-]*", template_name): raise ValueError("Invalid template name") ``` - Reject absolute paths, path separators, `.` segments, and `..` segments. - Resolve both paths and verify containment: ```python root = os.path.realpath(output_path) target = os.path.realpath(os.path.join(root, snake_name)) if os.path.commonpath([root, target]) != root: raise ValueError("Template path escapes output directory") ``` - Fail when the destination already exists. - Require an explicit `--force` option before overwriting any file. - Create files using exclusive mode (`'x'`) by default. - If the value can come from automation or network input, perform validation before any directory is created. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
references/api-reference.md:296
Finding
File Upload Example Omits Resource Authorization and File Security Controls<![CDATA[ ## Vulnerability Details **File Location**: `references/api-reference.md`, lines 296–316 **Vulnerability Type**: Missing object-level authorization and unrestricted file upload **Risk Level**: High ### Vulnerable Code ```typescript import { PutObjectCommand } from '@aws-sdk/client-s3'; protectedProcedure .input(z.object({ file: z.instanceof(File), resumeId: z.string(), })) .mutation(async ({ ctx, input }) => { const buffer = await input.file.arrayBuffer(); const key = `resumes/${ctx.user.id}/${input.resumeId}/${input.file.name}`; await s3Client.send(new PutObjectCommand({ Bucket: process.env.STORAGE_BUCKET, Key: key, Body: Buffer.from(buffer), ContentType: input.file.type, })); return { url: `https://${process.env.STORAGE_BUCKET}/${key}` }; }); ``` ### Technical Analysis `protectedProcedure` verifies that the caller is authenticated, but the example never loads the identified resume or verifies that `input.resumeId` belongs to `ctx.user.id`. Authentication alone does not provide object-level authorization. The entire file is buffered before any documented size check. The implementation also trusts the client-provided filename and MIME type, does not restrict extensions or content, and returns a directly constructed URL. Client filenames can contain path separators or special characters that produce confusing object keys. If uploaded objects are publicly served, active content may be delivered from application-controlled storage. ### Attack Path 1. An authenticated attacker obtains or guesses another user's resume identifier. 2. The attacker invokes the upload mutation with that identifier. 3. The procedure accepts the request because it checks only that the caller is logged in. 4. The attacker supplies an oversized file, misleading MIME type, dangerous active content, or crafted filename. 5. The server loads the entire file into memory and stores it without ownership or conte ...[truncated 705 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Query the resume before upload and verify that `resume.userId === ctx.user.id`. - Use a UUID schema for `resumeId` and return a generic not-found response where appropriate. - Enforce request and file-size limits before reading the complete body. - Stream uploads where possible instead of buffering the entire object in memory. - Allowlist required MIME types and extensions, and verify content using file signatures rather than trusting `file.type`. - Generate server-controlled random object names; do not place the raw client filename in the storage key. - Normalize and reject filenames containing separators, control characters, or ambiguous Unicode. - Store uploads as private objects and use short-lived signed URLs for authorized access. - Set safe response headers, including `Content-Disposition: attachment` and `X-Content-Type-Options: nosniff`, where files are downloaded. - Scan supported upload types for malware where the deployment threat model requires it. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
references/api-reference.md:346
Finding
PDF Export Example Allows Rendering Without Resume Ownership Verification<![CDATA[ ## Vulnerability Details **File Location**: `references/api-reference.md`, lines 346–360 **Vulnerability Type**: Broken object-level authorization **Risk Level**: High ### Vulnerable Code ```typescript protectedProcedure .input(z.object({ resumeId: z.string() })) .mutation(async ({ ctx, input }) => { const resumeUrl = `${process.env.APP_URL}/resume/${input.resumeId}`; const pdfBuffer = await generatePdf(resumeUrl); // 上传到存储或返回 return { pdf: pdfBuffer.toString('base64'), contentType: 'application/pdf', }; }); ``` ### Technical Analysis The endpoint requires an authenticated caller but directly embeds the supplied `resumeId` into a render URL. It does not query the resume, verify ownership, or evaluate an explicit sharing policy before instructing the printer service to load the page. This is an insecure direct object reference pattern. If the printer can access the resume route with greater trust than the requesting user, or if the route exposes resumes by identifier, the export endpoint becomes a proxy for unauthorized content retrieval. ### Attack Path 1. An authenticated attacker obtains or guesses another user's resume identifier. 2. The attacker submits the identifier to the PDF export mutation. 3. The procedure constructs the target URL without checking ownership. 4. The Browserless/Puppeteer service loads the target resume. 5. The generated PDF is returned as Base64 to the attacker. 6. The attacker decodes the response and obtains the victim's resume data. ### Impact Assessment Successful exploitation can disclose complete resume contents, potentially including names, addresses, phone numbers, email addresses, employment history, education, and other personal information. Access is limited to resumes reachable by the rendering route and printer context, but may affect any user whose resume identifier becomes known. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Load the resume by ID before rendering and enforce ownership: ```typescript const resume = await ctx.db.query.resumes.findFirst({ where: (resumes, { and, eq }) => and(eq(resumes.id, input.resumeId), eq(resumes.userId, ctx.user.id)), }); if (!resume) { throw new TRPCError({ code: "NOT_FOUND" }); } ``` - If public sharing is supported, evaluate an explicit and revocable share policy rather than assuming possession of an ID grants access. - Use UUID validation or another strict identifier schema. - Ensure the printer accesses a dedicated internal render route protected by a short-lived, resume-scoped token. - Do not give the printer a broad session or credential capable of reading arbitrary user records. - Record export events and rate-limit PDF generation to reduce enumeration and resource abuse. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
references/deployment.md:12
Finding
Deployment Examples Use Predictable Secrets and Mutable Container Tags<![CDATA[ ## Vulnerability Details **File Location**: `references/deployment.md`, lines 12–82 and 120–134; `SKILL.md`, lines 220–248 **Vulnerability Type**: Weak default credentials and unpinned dependencies **Risk Level**: Medium ### Vulnerable Code ```yaml services: app: image: amruthpillai/reactive-resume:latest ports: - "3000:3000" environment: - DATABASE_URL=postgresql://postgres:password@db:5432/reactive_resume - BETTER_AUTH_SECRET=your-secret-key-min-32-chars - PRINTER_ENDPOINT=ws://printer:3000?token=printer-token ``` ```yaml db: image: postgres:15-alpine environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=password - POSTGRES_DB=reactive_resume ``` ```yaml printer: image: browserless/chrome:latest environment: - TOKEN=printer-token ``` ```bash docker run -d \ -p 3000:3000 \ -e DATABASE_URL=postgresql://user:pass@host:5432/db \ -e BETTER_AUTH_SECRET=your-secret \ --name reactive-resume \ amruthpillai/reactive-resume:latest ``` The shorter Compose example in `SKILL.md` similarly uses: ```yaml image: amruthpillai/reactive-resume:latest ... - PRINTER_ENDPOINT=ws://printer:3000?token=1234567890 ... - POSTGRES_PASSWORD=pass ... image: browserless/chrome:latest ... - TOKEN=1234567890 ``` ### Technical Analysis The deployment-ready examples contain predictable database passwords, authentication secrets, and Browserless tokens. Although presented as placeholders, they are syntactically complete and may be deployed unchanged. The application and Browserless images use the mutable `latest` tag. A later pull may retrieve different code than the version originally reviewed or tested. The examples therefore provide no reproducible dependency identity and increase supply-chain drift. ### Attack Path #### Predictable-secret path 1. An operator copies the example without replacing every placeholder. 2. A database or printer endpoint becomes reachable through network exposure, later ...[truncated 1190 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace all example secrets with generated values before startup. - Document secure generation commands, such as a cryptographically secure 32-byte random value. - Add startup validation that rejects known placeholders including `password`, `pass`, `printer-token`, `1234567890`, and `your-secret`. - Store secrets in Docker secrets, protected environment files, or a dedicated secrets manager rather than committed Compose files. - Restrict the database and printer to internal networks and do not publish their ports unless explicitly necessary. - Assign separate, least-privileged credentials to each service. - Pin application and Browserless images to reviewed semantic versions, preferably immutable image digests: ```yaml image: vendor/image@sha256:reviewed_digest ``` - Use automated dependency monitoring and test updates before promotion. - Add container hardening such as non-root users, read-only filesystems where supported, dropped Linux capabilities, and explicit network policies. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (31)

Credential Access

High
Category
Privilege Escalation
Content
### 4. 配置环境变量

```bash
cp .env.example .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
### 4. 配置环境变量

```bash
cp .env.example .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
### 4. 配置环境变量

```bash
cp .env.example .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
### 4. 配置环境变量

```bash
cp .env.example .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
pnpm install

# 配置环境变量
cp .env.example .env
# 编辑 .env

# 构建
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Docker Socket Access

High
Category
Privilege Escalation
Content
- "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./letsencrypt:/letsencrypt

  app:
Confidence
98% confidence
Finding
Mounting `/var/run/docker.sock` into a container gives that container effective control over the Docker daemon, which commonly enables container breakout, access to other containers, host filesystem mounting, and root-level host compromise. In this deployment guide the intent is legitimate Traefik service discovery, but the capability is still highly dangerous if Traefik or any code execution path in that container is compromised.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
"""运行 shell 命令"""
    try:
        if capture:
            result = subprocess.run(command, shell=True, capture_output=True, text=True)
            return result.returncode, result.stdout, result.stderr
        else:
            subprocess.run(command, shell=True)
Confidence
97% confidence
Finding
Using subprocess.run with shell=True on a command string creates a direct parameter-abuse path. In this script, the psql command is built with an environment/.env-derived DATABASE_URL, so a crafted value containing shell separators could execute arbitrary OS commands under the privileges of the user running the reset tool.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
result = subprocess.run(command, shell=True, capture_output=True, text=True)
            return result.returncode, result.stdout, result.stderr
        else:
            subprocess.run(command, shell=True)
            return 0, "", ""
    except Exception as e:
        print_colored(f"Error: {e}", Colors.RED)
Confidence
90% confidence
Finding
This second shell=True usage keeps the same unsafe API available across the script. While present calls may currently be constant strings, the helper normalizes insecure command execution and makes future or indirect parameter abuse likely.

Credential Access

High
Category
Privilege Escalation
Content
db_url = os.environ.get('DATABASE_URL')
    if not db_url:
        # 尝试从 .env 读取
        if os.path.exists('.env'):
            with open('.env', 'r') as f:
                for line in f:
                    if line.startswith('DATABASE_URL='):
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
db_url = os.environ.get('DATABASE_URL')
    if not db_url:
        # 尝试从 .env 读取
        if os.path.exists('.env'):
            with open('.env', 'r') as f:
                for line in f:
                    if line.startswith('DATABASE_URL='):
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding
The skill clearly instructs the agent/user to perform shell actions, read/write files, and manipulate environment configuration, but it declares no explicit tool scope or permissions. That creates an overbroad execution surface where an agent could invoke capabilities beyond what the skill contract communicates, increasing the chance of unsafe or unintended system changes.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
This section tells the operator to start Docker services and even launch `dockerd` with sudo, but it provides no warning that these commands make system-level changes, create background services, and may require elevated privileges. In an agent context, omitting that warning makes accidental privileged execution more likely and reduces informed consent for impactful operations.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
```bash
# 启动 PostgreSQL 和 Browserless(PDF 导出必需)
sudo dockerd &>/var/log/dockerd.log &
sudo docker compose -f compose.dev.yml up -d postgres browserless
```
Confidence
93% confidence
Finding
The instruction to run `sudo dockerd` launches the Docker daemon with root privileges, which is a high-impact system action and expands the attack surface if executed by an agent or unsuspecting user. Because Docker effectively grants root-equivalent control over the host, normalizing this command without safeguards is dangerous.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
```bash
# 启动 PostgreSQL 和 Browserless(PDF 导出必需)
sudo dockerd &>/var/log/dockerd.log &
sudo docker compose -f compose.dev.yml up -d postgres browserless
```

### 4. 配置环境变量
Confidence
91% confidence
Finding
Running `sudo docker compose ... up` starts containers with elevated control over host resources and can change networking, volumes, and local services. In a skill document, presenting privileged commands as routine setup steps without guardrails increases the risk of unintended host modification.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
# Printer (Browserless for PDF)
# 获取 Docker bridge IP:
# sudo docker network inspect reactive_resume_default --format '{{range .IPAM.Config}}{{.Gateway}}{{end}}'
PRINTER_APP_URL=http://<GATEWAY_IP>:3000
PRINTER_ENDPOINT=ws://<GATEWAY_IP>:4000?token=1234567890
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
# Printer (Browserless for PDF)
# 获取 Docker bridge IP:
# sudo docker network inspect reactive_resume_default --format '{{range .IPAM.Config}}{{.Gateway}}{{end}}'
PRINTER_APP_URL=http://<GATEWAY_IP>:3000
PRINTER_ENDPOINT=ws://<GATEWAY_IP>:4000?token=1234567890
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
# Printer (Browserless for PDF)
# 获取 Docker bridge IP:
# sudo docker network inspect reactive_resume_default --format '{{range .IPAM.Config}}{{.Gateway}}{{end}}'
PRINTER_APP_URL=http://<GATEWAY_IP>:3000
PRINTER_ENDPOINT=ws://<GATEWAY_IP>:4000?token=1234567890
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The self-hosting example includes concrete database credentials and a fixed Browserless token without an explicit warning that these are placeholders only. Users may copy them directly into real deployments, leading to weak default credentials, exposed services, and trivial unauthorized access.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
This code hard-codes multiple user-visible section labels in English (for example, "Summary", "Work Experience", "Projects", "Education", "Skills") while the surrounding file comments and metadata are in Chinese. That creates a locale policy concern because the rendered output forces one language for end users without any opt-in or configuration path.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
This markdown file uses Chinese throughout, including the title and all instructional content, but does not indicate that the language is optional or that the skill is intended only for a Chinese-speaking audience. Under the language/locale policy, forcing a specific language without user opt-in is a natural-language policy violation.

Rp1

Medium
Category
MCP Rug Pull
Confidence
75% confidence
Finding
Docker image references without a specific tag (:latest is implicit) or digest (@sha256:...) can be silently replaced by a malicious image.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
```bash
# 安装 certbot
sudo apt install certbot python3-certbot-nginx

# 获取证书
sudo certbot --nginx -d resume.yourdomain.com
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
```bash
# 安装 certbot
sudo apt install certbot python3-certbot-nginx

# 获取证书
sudo certbot --nginx -d resume.yourdomain.com
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
```bash
# 安装 certbot
sudo apt install certbot python3-certbot-nginx

# 获取证书
sudo certbot --nginx -d resume.yourdomain.com
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This markdown file includes commands that can overwrite database contents during restore and permanently delete old backup files, but the surrounding text does not warn users about data loss or irreversible effects. For markdown files, safety-impacting behaviors that affect user data should be disclosed clearly.

Static analysis

No suspicious patterns detected.