Back to skill

Security audit

quiz-maker

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real quiz-making skill, but it exposes document content, API keys, public quiz data, and host deployment controls in ways that are under-scoped and unsafe by default.

Install only after review. Do not use confidential documents unless you accept cloud transmission to the configured services, require authentication before exposing the web app, remove the TLS bypass, avoid running it as root, replace curl-to-bash and runtime package installs with pinned setup steps, and protect the ARK API key with a proper secret mechanism.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
Findings (8)

T03 · Remote Payload Retrieval and Execution

Error
Location
deploy/deploy.sh:1
Finding

Mutable Remote Script Is Executed Directly with Administrative Privileges

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
deploy/deploy.sh:25
Finding

Web Application Is Installed as a Persistent Root Service

Content
View full analysis
/etc/systemd/system/quiz-maker.service << 'EOF' [Unit] Description=Quiz Maker Server After=network.target [Service] Type=simple WorkingDirectory=/opt/quiz-maker ExecStart=/usr/bin/node server.js Restart=always RestartSec=5 User=root [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable quiz-maker ``` ### Technical Analysis The deployment creates a systemd service that runs the network-facing Node.js application as `root`, automatically restarts it, and enables it at boot. Document parsing, HTTPS requests, SQLite access, QR generation, and listening behind a reverse proxy do not require root privileges. Running this application as root violates least privilege and converts any application-level code execution or dependency compromise into full operating-system compromise. Enabling a production server at startup can be operationally reasonable, but it is not safe in this configuration because the service receives unrestricted root privileges and lacks systemd sandboxing directives. ### Attack Path 1. An attacker reaches the publicly exposed application. 2. The attacker exploits a future vulnerability in Express, a document parser, a native dependency, or application code. 3. The resulting process-level execution occurs in the service context. 4. Because the service uses `User=root`, the attacker immediately obtains root privileges. 5. Because the service is enabled and configured with `Restart=always`, malicious modifications may continue across process failures and system reboots. ### Impact Assessment Exploitation can provide root control over the host, including access to the SQLite database, uploaded documents, ARK API credentials inherited by the process, service definitions, network configuration, and all other users' ...[truncated 94 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
server.js:92
Finding

Unauthenticated API Permits Quiz Enumeration, Result Disclosure, Creation, and Deletion

Content
View full analysis
{ const db = require('./db').getDb(); const quiz = db.prepare(`SELECT * FROM quizzes WHERE id = ?`).get(req.params.id); if (!quiz) return res.status(404).json({ error: 'Quiz不存在' }); const submissions = db.prepare(`SELECT id, name, class_name, score, answers, submitted_at FROM submissions WHERE quiz_id = ? ORDER BY score DESC, submitted_at ASC`).all(req.params.id); const stats = db.prepare(`SELECT * FROM quiz_stats WHERE quiz_id = ?`).get(req.params.id); const questions = JSON.parse(quiz.questions); const avgScore = stats && stats.total_participants > 0 ? Math.round(stats.total_score / stats.total_participants) : 0; const questionStats = questions.map((q, i) => ({ index: i, question: q.question.substring(0, 60) + (q.question.length > 60 ? '...' : ''), accuracy: submissions.length > 0 ? Math.round(submissions.filter(s => JSON.parse(s.answers || '[]')[i] === q.correctAnswer).length / submissions.length * 100) : 0, correctAnswer: q.correctAnswer, options: q.options })); res.json({ quizId: quiz.id, title: quiz.title, description: quiz.description, totalSubmissions: submissions.length, avgScore, maxScore: stats?.max_score || 0, minScore: stats?.min_score || 0, submissions: submissions.map((s, idx) => ({ rank: idx + 1, name: s.name, className: s.class_name, score: s.score, submittedAt: s.submitted_at })), questionStats, createdAt: quiz.created_at }); }); // 6. 列表 app.get('/api/quizzes', (req, res) => { const db = require('./db').getDb(); res.json(db.prepare(`SELECT q.id, q.title, q.description, q.created_at, COALESCE(s.total_participants,0) as total_participants, COALESCE(s.total_score*1.0/NULLIF(s.total_participants,0),0) as avg_score FROM quizzes q LEFT JOIN ...[truncated 2075 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
quiz-create.js:10
Finding

TLS Certificate Verification Is Disabled When Transmitting Document Content

Content
View full analysis
{ const body = JSON.stringify({ content, title, description }); const options = { hostname: CLOUD_HOST, port: CLOUD_PORT, path: '/api/quiz/create-with-qr', method: 'POST', rejectUnauthorized: false, headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) } }; const req = https.request(options, (res) => { let data = ''; res.on('data', chunk => data += chunk); res.on('end', () => { ``` ### Technical Analysis Setting `rejectUnauthorized: false` disables normal TLS certificate-chain and hostname validation. Although the request uses HTTPS, the client accepts a certificate presented by any endpoint. The request body contains the source document's extracted text, title, and description. A network attacker able to intercept or redirect traffic can impersonate the configured server, read the document, and return a forged quiz response. The use of a hardcoded IP address likely motivated the certificate bypass, but it does not provide server authenticity. ### Attack Path 1. A user invokes `quiz-create.js` with document content. 2. An attacker controls or intercepts the network path, DNS/routing environment, proxy configuration, or local gateway. 3. The attacker presents an arbitrary TLS certificate. 4. The client accepts it because certificate validation is disabled. 5. The attacker receives the full JSON request body and can return attacker-controlled JSON. 6. The caller processes the forged response and may distribute a malicious or incorrect quiz URL. ### Impact Assessment An attacker can ...[truncated 244 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
public/admin.html:112
Finding

Stored Cross-Site Scripting Through Quiz and Participant Data

Content
View full analysis

${data.title}

创建于 ${new Date(data.createdAt).toLocaleString('zh-CN')} · ${data.description || ''}

``` Participant records are also directly interpolated into the same HTML assignment: ```javascript return ` ${s.rank} ${s.name} ${s.className || '-'} ${s.score}分 ${new Date(s.submittedAt).toLocaleString('zh-CN')} `; ``` Question text is rendered in the same way: ```javascript : data.questionStats.map(q => `
${q.index+1}. ${q.question}
${q.accuracy}%
`).join('') ``` The participant page has equivalent unsafe rendering at `public/quiz.html:102-169` and `public/quiz.html:231-255`. ### Technical Analysis Quiz titles and descriptions, participant names and class names, and AI-generated question content are inserted into template strings assigned to `innerHTML` without output encoding or sanitization. These fields are persisted by unauthenticated endpoints. For example, `server.js:70-86` accepts `name` and `className` from a submission and stores them without validation. Quiz title and description are likewise stored during creation. An attacker can submit HTML containing an executable event handler, SVG payload, or other browser-interpreted markup. The payload remains in SQLite and ...[truncated 1161 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
server.js:16
Finding

Failed Uploads Are Not Deleted, Enabling Persistent Disk Exhaustion

Content
View full analysis
{ try { if (!req.file) return res.status(400).json({ error: '请上传文件' }); const filePath = req.file.path; const fileExt = path.extname(req.file.originalname).toLowerCase(); const content = await parseDocument(filePath, fileExt); if (!content || content.trim().length < 50) { try { fs.unlinkSync(filePath); } catch(e) {} return res.status(400).json({ error: '文档内容过少' }); } const questions = await generateQuestions(content); try { fs.unlinkSync(filePath); } catch(e) {} const db = require('./db').getDb(); const quizId = require('uuid').v4(); const title = req.body.title || `Quiz ${new Date().toLocaleString('zh-CN')}`; const description = req.body.description || ''; db.prepare(`INSERT INTO quizzes (id, title, description, questions, created_at) VALUES (?, ?, ?, ?, datetime('now'))`).run(quizId, title, description, JSON.stringify(questions)); db.prepare(`INSERT INTO quiz_stats (quiz_id) VALUES (?)`).run(quizId); res.json({ success: true, quizId, title, questionCount: questions.length }); } catch (err) { console.error('Create error:', err); res.status(500).json({ error: err.message }); } }); ``` The upload limit is defined at `upload-handler.js:17-26`: ```javascript const upload = multer({ storage, fileFilter: (req, file, cb) => { const ext = path.extname(file.originalname).toLowerCase(); if (!ALLOWED_TYPES.includes(ext)) { return cb(new Error(`不支持的文件类型: ${ext},仅支持 .docx .md .pptx`)); } cb(null, true); }, limits: { fileSize: 20 * 1024 * 1024 } // 20MB }); ``` ### Technical Analysis Uploaded files are deleted after successful question generation and when parsed content is to ...[truncated 1065 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
start.sh:13
Finding

Environment Configuration File Is Executed as Shell Code

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
qr-gen.py:1
Finding

QR Utility Automatically Installs Unpinned Packages at Runtime

Content
View full analysis
Remediation
View remediation
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
  • Rogue AgentSelf-Modification, Session Persistence
Findings (70)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The skill description emphasizes local quiz generation from documents and QR return, but the workflow sends full document content to an external cloud API and does not clearly disclose that data leaves the local environment. This mismatch is dangerous because users may provide sensitive documents under the assumption of local-only processing, causing unanticipated data exposure to a third party.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill description emphasizes local quiz generation from documents and QR return, but the workflow sends full document content to an external cloud API and does not clearly disclose that data leaves the local environment. This mismatch is dangerous because users may provide sensitive documents under the assumption of local-only processing, causing unanticipated data exposure to a third party.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill description emphasizes local quiz generation from documents and QR return, but the workflow sends full document content to an external cloud API and does not clearly disclose that data leaves the local environment. This mismatch is dangerous because users may provide sensitive documents under the assumption of local-only processing, causing unanticipated data exposure to a third party.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

The skill description emphasizes local quiz generation from documents and QR return, but the workflow sends full document content to an external cloud API and does not clearly disclose that data leaves the local environment. This mismatch is dangerous because users may provide sensitive documents under the assumption of local-only processing, causing unanticipated data exposure to a third party.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill description emphasizes local quiz generation from documents and QR return, but the workflow sends full document content to an external cloud API and does not clearly disclose that data leaves the local environment. This mismatch is dangerous because users may provide sensitive documents under the assumption of local-only processing, causing unanticipated data exposure to a third party.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The workflow sends the supplied document content to a cloud API to generate quizzes, but the skill does not provide a clear user-facing warning about data sharing, retention, or privacy consequences. This is especially dangerous because the input source is documents, which commonly contain confidential educational, business, or personal information.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: @xmldom/xmldom==0.8.13 — 10 advisory(ies): CVE-2026-83608 (xmldom: DocType `name` Injection Bypasses requireWellFormed); CVE-2026-83605 (xmldom: Attribute name injection via setAttribute() bypasses requireWellFormed); CVE-2026-83610 (xmldom: XML fragment injection via invalid EntityReference.nodeName during requi) +7 more

High
Category
Supply Chain
Confidence
96% confidence
Finding

The lockfile pins @xmldom/xmldom 0.8.13, which is flagged with multiple high-severity XML/DOM injection advisories. In a skill that processes uploaded documents such as DOCX, XML parsing is directly in scope, so malformed document content could potentially trigger parser flaws and compromise document handling or enable downstream injection behaviors.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: adm-zip==0.5.17 — 2 advisory(ies): CVE-2026-76845 (adm-zip extraction follows destination symlinks, allowing arbitrary file overwri); CVE-2026-39244 (adm-zip: Crafted ZIP file triggers 4GB memory allocation)

High
Category
Supply Chain
Confidence
97% confidence
Finding

adm-zip 0.5.17 is reported vulnerable to symlink-following during extraction and to excessive memory allocation on crafted ZIP input. Because this skill ingests user-supplied documents and DOCX files are ZIP-based, this dependency is especially risky: an attacker could supply a malicious archive to cause denial of service or arbitrary file overwrite depending on usage.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: multer==2.1.1 — 5 advisory(ies): CVE-2026-5038 (Multer vulnerable to Denial of Service via incomplete cleanup of aborted uploads); CVE-2026-82333 (multer vulnerable to Denial of Service via oversized array index in field names); CVE-2026-5079 (Multer vulnerable to Denial of Service via deeply nested field names) +2 more

High
Category
Supply Chain
Confidence
98% confidence
Finding

multer 2.1.1 is associated with multiple denial-of-service issues involving malformed multipart field structures and aborted uploads. This is highly relevant because the skill accepts uploaded files; a remote attacker could exploit upload handling to exhaust server resources or destabilize the service without authentication.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: adm-zip==0.5.17 — 2 advisory(ies): CVE-2026-76845 (adm-zip extraction follows destination symlinks, allowing arbitrary file overwri); CVE-2026-39244 (adm-zip: Crafted ZIP file triggers 4GB memory allocation)

High
Category
Supply Chain
Confidence
97% confidence
Finding

adm-zip 0.5.17 is flagged with high-severity advisories including symlink-following during extraction and crafted ZIP-induced excessive memory allocation. In this skill's context, document ingestion and archive handling make this more dangerous because an uploaded malicious archive could trigger file overwrite or denial-of-service conditions on the host processing service.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: multer==2.1.1 — 5 advisory(ies): CVE-2026-5038 (Multer vulnerable to Denial of Service via incomplete cleanup of aborted uploads); CVE-2026-82333 (multer vulnerable to Denial of Service via oversized array index in field names); CVE-2026-5079 (Multer vulnerable to Denial of Service via deeply nested field names) +2 more

High
Category
Supply Chain
Confidence
98% confidence
Finding

multer 2.1.1 is associated with multiple denial-of-service advisories involving aborted uploads and malformed field names. This skill explicitly appears to accept user-supplied documents for quiz generation, so a vulnerable upload parser materially increases exposure to remote service disruption by unauthenticated or low-effort attackers.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The DELETE endpoint allows any unauthenticated requester to remove a quiz and all related submissions and statistics. This is a direct integrity and availability issue because an attacker can permanently destroy quiz content and participant records without needing credentials.

Content

No source excerpt is available for this finding.

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 · start.sh (reported line 7)May include surrounding context.

sh
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
LOG_DIR="$SCRIPT_DIR/logs"
ENV_FILE="$SCRIPT_DIR/.env"
mkdir -p "$LOG_DIR"

echo "🚀 启动 Quiz Maker..."

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 · start.sh (reported line 13)May include surrounding context.

sh
echo "🚀 启动 Quiz Maker..."
echo "📁 工作目录: $SCRIPT_DIR"

# 加载环境变量(如果有 .env 文件)
if [ -f "$ENV_FILE" ]; then
  echo "📋 加载环境变量: $ENV_FILE"
  set -a

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 · start.sh (reported line 20)May include surrounding context.

sh
echo "🚀 启动 Quiz Maker..."
echo "📁 工作目录: $SCRIPT_DIR"

# 加载环境变量(如果有 .env 文件)
if [ -f "$ENV_FILE" ]; then
  echo "📋 加载环境变量: $ENV_FILE"
  set -a

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill declares no explicit tool scope even though it instructs use of shell commands and environment/config modification. In an agent setting, missing scope boundaries increases the chance of unintended command execution or privilege creep because the runtime cannot clearly constrain what the skill is allowed to do.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

Broad trigger phrases like '出题' or '生成题目' can match many ordinary educational requests and cause the skill to activate unexpectedly. In this skill, unintended invocation matters more because activation can lead to document extraction, cloud upload, local config changes, and credential prompting.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The skill instructs the agent to ask for an API key and persist it in a local .env file. Collecting and storing long-lived credentials expands the skill's sensitivity and creates risk of accidental disclosure, misuse by other local processes, or later exfiltration if the workspace is compromised.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The public description presents a simple document-to-quiz utility, but the instructions also modify a local configuration file and require a service restart. Hidden state changes are risky in agent workflows because they alter the host environment beyond the user's likely expectation and can affect future runs or other services.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script makes privileged system-wide changes immediately, including installing packages, writing a systemd unit, enabling persistence, and restarting services, without any confirmation, dry-run mode, or prerequisite checks. This is dangerous because an operator can unintentionally modify a host, disrupt existing services, or deploy into the wrong environment with root-level impact.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The manifest describes a quiz-generation tool that creates tests from documents and returns a QR code for answering. This script performs host-level server provisioning by installing Nginx and Certbot, creating a systemd unit, and running the application as a persistent root service, which is infrastructure management rather than an obvious capability of a quiz authoring skill itself.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
85% confidence
Finding

Enabling the service with systemd creates boot persistence for a process that is configured to run as root. Persistence itself can be legitimate in deployment, but in security terms it increases the blast radius of any compromise in the application because the service will automatically restart and survive reboots.

Content

Scanner excerpt · deploy/deploy.sh (reported line 43)May include surrounding context.

sh
EOF

systemctl daemon-reload
systemctl enable quiz-maker
echo "Systemd 配置完成"

echo "=== 7/10 启动服务 ==="

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The manifest says the skill generates quizzes from documents such as docx, pdf, txt, but this parser only supports .docx, .md, and .pptx and explicitly rejects other extensions. That is a direct behavior mismatch with the described input scope, especially for advertised formats like pdf and txt.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The PPTX fallback executes a shell pipeline via execSync and interpolates filePath directly into the command string. Even though the path is quoted, shell metacharacters such as embedded double quotes or command substitutions can still break out of the quoting context, enabling command injection if an attacker controls the file path; additionally, invoking external utilities increases attack surface and can block the process.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The code falls back to executing a shell command with execSync to extract strings from the file, but there is no confirmation prompt, user-visible logging, or warning comment/docstring indicating that external command execution may occur. Subprocess execution is a safety-relevant operation under this rule, and the top-level function description does not disclose this behavior.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.insecure_tls_verification

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
parser.js:73

HTTPS certificate verification is disabled.

Warn
Code
suspicious.insecure_tls_verification
Location
quiz-create.js:20