Back to skill

Security audit

Deepaistudy Errors

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it claims, but it handles account credentials and student worksheet images with weak safeguards and one misleading preview control.

Review this before installing if you will use real student work or account credentials. Use only a trusted HTTPS DeepAIStudy server, avoid shared machines, assume uploaded images and extracted content may be processed and retained remotely, and do not rely on --preview to prevent upload or saving until the implementation is fixed.

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
deepaistudy_errors.py:43
Finding

Credentials Are Stored in a Plaintext Configuration File and Password Values Are Echoed

Content
View full analysis
configparser.ConfigParser: config = configparser.ConfigParser() CONFIG_DIR.mkdir(parents=True, exist_ok=True) if CONFIG_FILE.exists(): config.read(CONFIG_FILE) if not config.has_section("default"): config.add_section("default") return config def save_config(config: configparser.ConfigParser) -> None: CONFIG_DIR.mkdir(parents=True, exist_ok=True) with open(CONFIG_FILE, "w") as f: config.write(f) ``` The configuration command also prints the supplied value without checking whether it is sensitive: ```python def cmd_config(args: argparse.Namespace) -> None: """Configuration command""" if args.subcommand == "set": if not args.key or not args.value: print("Usage: deepaistudy-errors config set ") sys.exit(1) set_config_value(args.key, args.value) print(f"✓ Set {args.key} = {args.value}") ``` ### Technical Analysis The password is stored directly in an INI file without encryption or integration with an operating-system credential manager. The file is created using the process's current `umask`, and the code does not explicitly enforce owner-only permissions such as `0600`. If the file already exists with permissive permissions, rewriting it does not correct those permissions. The `config list` implementation masks the password, but `config set password ` immediately prints the unmasked value. This may expose the password through terminal capture, CI logs, agent transcripts, shell wrappers, or monitoring systems. Supplying the password as a command ...[truncated 1331 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
deepaistudy_errors.py:402
Finding

The Preview Option Does Not Prevent Remote Upload or Record Creation

Content
View full analysis
None: """Add an error-book entry""" client = _get_client() images = args.images if not images: print("Error: specify an image path") sys.exit(1) for img in images: if not os.path.exists(img): print(f"Error: image does not exist: {img}") sys.exit(1) subject = args.subject or "General" title = args.title or "" print(f"Submitting {len(images)} images for AI analysis...") task_id = client.submit_error_analysis(images, subject, title) ``` However, the command-line interface advertises options that imply different behavior: ```python add_sp.add_argument("--auto-add", action="store_true", help="Automatically add all recognized errors (default)") add_sp.add_argument("--preview", action="store_true", help="Only preview recognition results; do not save to the error book") add_sp.add_argument("--count", "-n", type=int, default=0, help="Maximum number of errors to add (0 means all)") ``` ### Technical Analysis `cmd_add` never checks `args.preview`, `args.auto_add`, or the add command's `args.count`. Consequently, invoking the command with `--preview` follows exactly the same path as an ordinary add operation. `submit_error_analysis()` calls `add_error_with_ai()`, which uploads the image files to `/api/mobile/breakthrough/ingest-analyze`. The declared semantics of that endpoint and the surrounding code indicate that this workflow adds error-book records while performing analysis. This violates the privacy expectation established by the option description. A user may select preview mode specifi ...[truncated 1130 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
deepaistudy_errors.py:80
Finding

An Arbitrary Configured Server Receives Account Credentials, Tokens, and Student Images

Content
View full analysis
None: """Log in and obtain a JWT token""" if not self.username or not self.password: raise ValueError("Username or password is not configured") resp = requests.post( f"{self.server}/api/mobile/login", json={"username": self.username, "password": self.password}, timeout=30, ) ``` Uploaded images and bearer tokens are subsequently sent to the same configured destination: ```python resp = requests.post( f"{self.server}/api/mobile/breakthrough/ingest-analyze", headers={ "Authorization": f"Bearer {self.token}", }, data=data, files=files, timeout=60, ) ``` The server is loaded without scheme or host validation: ```python def _get_client() -> DeepAIStudyClient: server = get_config_value("server") username = get_config_value("username") password = get_config_value("password") if not server: raise ValueError("Server address is not configured") if not username or not password: raise ValueError("Username or password is not configured") return DeepAIStudyClient(server, username, password) ``` ### Technical Analysis Remote communication is necessary for the Skill's declared AI-analysis functionality, and the documented destination is `ht ...[truncated 2110 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (19)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill instructs users to upload photos of students' error-question sheets for AI analysis, but it does not clearly warn that these images and extracted study content are sent to a remote third-party service. Because such photos can contain personal data, school information, handwriting, and educational records, the omission undermines informed consent and can lead to unintended privacy disclosure.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Credentials are stored in a local plaintext INI file under the user's home directory with no warning, encryption, or permission hardening. On shared systems or compromised accounts, this can expose the service password and enable account takeover or further access to the user's remote data.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · deepaistudy_errors.py (reported line 91)May include surrounding context.

python
"""登录获取 JWT token"""
        if not self.username or not self.password:
            raise ValueError("未配置用户名或密码,请先运行: deepaistudy-errors config set username <邮箱>")
        resp = requests.post(
            f"{self.server}/api/mobile/login",
            json={"username": self.username, "password": self.password},
            timeout=30,

Tainted flow: 'note_id' from requests.post (line 444, network input) → requests.get (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · deepaistudy_errors.py (reported line 156)May include surrounding context.

python
def get_error_book(self, note_id: int) -> Dict[str, Any]:
        """获取错题详情"""
        resp = requests.get(
            f"{self.server}/api/mobile/error-book/{note_id}",
            headers=self._headers(),
            timeout=30,

Tainted flow: 'note_id' from requests.post (line 240, network input) → requests.get (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · deepaistudy_errors.py (reported line 162)May include surrounding context.

python
timeout=30,
        )
        if self._refresh_token_if_needed(resp):
            resp = requests.get(
                f"{self.server}/api/mobile/error-book/{note_id}",
                headers=self._headers(),
                timeout=30,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The add workflow uploads local image files to a remote server for AI analysis without an explicit privacy warning at the point of upload. Because study images may contain personal information, grades, handwritten notes, or other sensitive content, silent transmission increases the risk of unintentional data disclosure.

Content

No source excerpt is available for this finding.

Tainted flow: 'data' from requests.post (line 99, network input) → requests.post (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · deepaistudy_errors.py (reported line 190)May include surrounding context.

python
if title:
            data["title"] = title

        resp = requests.post(
            f"{self.server}/api/mobile/breakthrough/ingest-analyze",
            headers={
                "Authorization": f"Bearer {self.token}",

Tainted flow: 'data' from requests.post (line 99, network input) → requests.post (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · deepaistudy_errors.py (reported line 209)May include surrounding context.

python
files.append(
                    ("images", (os.path.basename(img_path), open(img_path, "rb"), "image/jpeg"))
                )
            resp = requests.post(
                f"{self.server}/api/mobile/breakthrough/ingest-analyze",
                headers={"Authorization": f"Bearer {self.token}"},
                data=data,

Tainted flow: 'note_id' from requests.post (line 240, network input) → requests.put (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · deepaistudy_errors.py (reported line 303)May include surrounding context.

python
def mark_mastered(self, note_id: int) -> Dict[str, Any]:
        """标记为已掌握"""
        resp = requests.put(
            f"{self.server}/api/mobile/error-book/{note_id}/master",
            headers=self._headers(),
            timeout=30,

Tainted flow: 'note_id' from requests.post (line 240, network input) → requests.put (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · deepaistudy_errors.py (reported line 309)May include surrounding context.

python
def mark_mastered(self, note_id: int) -> Dict[str, Any]:
        """标记为已掌握"""
        resp = requests.put(
            f"{self.server}/api/mobile/error-book/{note_id}/master",
            headers=self._headers(),
            timeout=30,

Tainted flow: 'note_id' from requests.post (line 240, network input) → requests.put (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · deepaistudy_errors.py (reported line 320)May include surrounding context.

python
def mark_unmastered(self, note_id: int) -> Dict[str, Any]:
        """取消已掌握标记"""
        resp = requests.put(
            f"{self.server}/api/mobile/error-book/{note_id}/unmaster",
            headers=self._headers(),
            timeout=30,

Tainted flow: 'note_id' from requests.post (line 240, network input) → requests.put (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · deepaistudy_errors.py (reported line 326)May include surrounding context.

python
def mark_unmastered(self, note_id: int) -> Dict[str, Any]:
        """取消已掌握标记"""
        resp = requests.put(
            f"{self.server}/api/mobile/error-book/{note_id}/unmaster",
            headers=self._headers(),
            timeout=30,

Tainted flow: 'note_id' from requests.post (line 240, network input) → requests.post (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · deepaistudy_errors.py (reported line 342)May include surrounding context.

python
count: int = 3,
    ) -> Dict[str, Any]:
        """生成变式题"""
        resp = requests.post(
            f"{self.server}/api/mobile/error-book/{note_id}/variation",
            headers=self._headers(),
            json={"difficulty": difficulty, "count": count},

Tainted flow: 'note_id' from requests.post (line 240, network input) → requests.post (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · deepaistudy_errors.py (reported line 349)May include surrounding context.

python
timeout=60,
        )
        if self._refresh_token_if_needed(resp):
            resp = requests.post(
                f"{self.server}/api/mobile/error-book/{note_id}/variation",
                headers=self._headers(),
                json={"difficulty": difficulty, "count": count},

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The delete command performs an irreversible remote deletion immediately, with no confirmation prompt or safety flag. In a CLI that operates on remote records, this raises the risk of accidental destructive actions through user error, scripting mistakes, or copy/paste mishaps.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The CLI advertises --preview and --count/limited-add semantics, but cmd_add always submits images for analysis and then saves all recognized errors. This mismatch can cause unintended remote upload and persistent creation of records when a user expects a dry run or a limited subset, making the operation more dangerous because it handles sensitive study materials and performs state-changing remote actions.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

All user-facing instructions and command explanations are presented in Chinese, with no indication that the skill supports other languages or that Chinese-only operation is a documented regional requirement. This can violate language or locale policy where user opt-in or explicit justification is required.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The skill exposes a delete command for study records without warning that the action may permanently remove stored notes or associated analysis results. Users may assume deletion is reversible or local-only, which can cause accidental loss of educational data.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The module description and command-facing text are written entirely in Chinese, which imposes a specific language on all users. Under the stated policy, a forced language or locale without user choice is a natural-language policy violation unless it is explicitly documented as region-specific or opt-in.

Content

No source excerpt is available for this finding.