Back to skill

Security audit

sg-property-scraper

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent Singapore property scraper with expected network use and optional commute lookup, but users should be mindful of third-party address sharing and unpinned dependencies.

Install in a virtual environment, avoid running pip as an administrator, and consider pinning dependency versions. Use --commute-to only when you are comfortable sending the destination address and scraped property addresses to Google Routes API. Avoid --no-validate and --raw-param unless you are intentionally testing unsupported PropertyGuru query parameters.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (2)

T08 · Insecure Dependencies

Warning
Location
SKILL.md:23
Finding

Unpinned Third-Party Dependencies in Skill Installation Instructions

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:23-27
Vulnerability Type: Supply-chain exposure through unpinned dependencies
Risk Level: Medium

Vulnerable Code Snippet:

markdown
## Dependencies

- Python 3.8+
- `pip install curl_cffi beautifulsoup4 lxml`
- Optional: `GOOGLE_MAPS_API_KEY` env var for commute time calculation (Google Routes API)

Technical Analysis

The Skill directs users or automated agents to install curl_cffi, beautifulsoup4, and lxml without specifying reviewed versions, cryptographic hashes, a lockfile, or an explicit trusted package index.

Package names are legitimate and no direct evidence indicates that they are currently malicious. However, unconstrained resolution allows installation behavior to change after the Skill has been audited. A future compromised release, package-index compromise, dependency compromise, or unexpected breaking release could introduce attacker-controlled code.

Python package installation may execute build backend code for source distributions. Installed packages are also imported by scripts/scrape.py, allowing malicious module initialization code to execute when the scraper runs.

Attack Path

  1. A user or agent follows the dependency installation command in SKILL.md.
  2. pip resolves the latest available versions and transitive dependencies from its configured package index.
  3. An upstream package, transitive dependency, release artifact, or configured index is compromised.
  4. Malicious code executes during source-package building or when the installed module is subsequently imported.
  5. The code runs with the privileges of the account performing installation or running the Skill.

Impact Assessment

Successful exploitation could provide arbitrary code execution under the installing or executing user's account. The resulting access could include reading files and environment variables available to that account, mod ...[truncated 302 chars]

Remediation
View remediation

Remediation Suggestions

  1. Provide a reviewed and version-pinned dependency file, for example:
    text
    curl_cffi==<reviewed-version>
    beautifulsoup4==<reviewed-version>
    lxml==<reviewed-version>
    
  2. Generate and verify SHA-256 hashes for every direct and transitive dependency, then install with:
    bash
    python3 -m pip install --require-hashes -r requirements.txt
    
  3. Use a lockfile generated by a dependency-management tool so transitive versions are reproducible.
  4. Specify a trusted package index explicitly and prevent unintended fallback to additional indexes.
  5. Run dependency installation and the scraper in an isolated virtual environment or restricted container.
  6. Add automated dependency vulnerability and provenance scanning to the release process.
  7. Keep SKILL.md synchronized with the hardened installation procedure rather than retaining the unconstrained command.

T08 · Insecure Dependencies

Warning
Location
README.md:5
Finding

Unpinned Third-Party Dependencies in README Setup Command

Content
View full analysis

Vulnerability Details

File Location: README.md:5-10
Vulnerability Type: Supply-chain exposure through unpinned dependencies
Risk Level: Medium

Vulnerable Code Snippet:

markdown
## Setup

```bash
# Python 3.8+ required
pip install curl_cffi beautifulsoup4 lxml
text

### Technical Analysis

The documented setup command resolves mutable package versions without a lockfile, exact version constraints, artifact hashes, or an explicitly trusted index. Consequently, two installations performed at different times can install different code even though the audited project files have not changed.

The risk includes compromise of a direct dependency, compromise of a transitive dependency, malicious artifacts from an incorrectly configured package index, and execution of unreviewed build backend logic. The scraper later imports these dependencies, so malicious code can also execute at application startup.

The command does not itself prove malicious intent, and the named dependencies are consistent with the declared scraping functionality. The issue is the absence of controls needed to make dependency installation reproducible and auditable.

### Attack Path

1. A user copies the setup command from `README.md`.
2. `pip` selects current package releases and their transitive dependencies.
3. A selected distribution or dependency has been compromised or replaced in the configured index.
4. Attacker-controlled code runs during package construction, installation, or a later import by `scripts/scrape.py`.
5. The attacker obtains code execution in the context used to install or run the project.

### Impact Assessment

Exploitation could allow access to user-readable data, environment variables, project files, network connectivity, and credentials available to the process. It could also permit modification of files writable by the current account.

The maximum privilege is normally that of the user executin
...[truncated 165 chars]
Remediation
View remediation

Remediation Suggestions

  1. Replace the direct installation command with installation from a reviewed, version-pinned requirements file.
  2. Pin all transitive dependencies and include cryptographic hashes for downloaded artifacts.
  3. Use python3 -m pip from a dedicated virtual environment rather than an ambiguous global pip executable.
  4. Document a trusted index URL and prohibit unreviewed extra indexes.
  5. Add guidance not to run dependency installation as root or an administrator.
  6. Periodically update pinned versions through a controlled process that includes vulnerability scanning and code review.
  7. Ensure the README and SKILL.md reference the same locked dependency manifest.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
Findings (8)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% confidence
Finding

Exposing --no-validate and arbitrary --raw-param options creates a parameter-smuggling surface that can bypass intended input constraints and allow unreviewed query parameters to be sent upstream. In an agent context, this weakens guardrails, can trigger unexpected remote behavior, and may facilitate abuse of the target service or accidental leakage of sensitive values into requests.

Content

Scanner excerpt · SKILL.md (reported line 97)May include surrounding context.

md
|------|-------------|
| `--pages N` | Number of pages to scrape (default: 1) |
| `--dry-run` | Build and print URL(s), skip scraping |
| `--no-validate` | Skip parameter validation |
| `--timeout N` | HTTP request timeout in seconds (default: 30) |
| `--raw-param K=V` | Extra URL query param (repeatable) |
| `--output json\|text\|none` | Output format (default: json when piped) |

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
87% confidence
Finding

The --no-validate flag disables input validation, and the tool also supports raw query parameter injection via --raw-param. In an agent context, this increases the chance of policy bypass, unbounded or unsupported query construction, and misuse of the scraper against the upstream service because safety constraints can be intentionally disabled by callers.

Content

Scanner excerpt · scripts/scrape.py (reported line 558)May include surrounding context.

python
help="Number of pages to scrape (default: 1)")
    e.add_argument("--dry-run", action="store_true",
                   help="Build and print URL(s) only, skip scraping")
    e.add_argument("--no-validate", action="store_true",
                   help="Skip parameter validation")
    e.add_argument("--timeout", type=int, default=30,
                   help="HTTP request timeout in seconds (default: 30)")

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill declares executable/network-capable behavior but does not constrain or document tool scope with explicit permissions or allowed-tools metadata. In an agent ecosystem, that increases the chance the skill is invoked with broader-than-necessary access to environment variables, local files, and outbound network requests, weakening least-privilege controls.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The description says to use the skill when asked to "search Singapore properties" or "find rental or sale listings," which are broad natural-language triggers without clear exclusion conditions. This may cause unintended invocation for general discussion or advice requests about Singapore property rather than explicit scraping/search tasks.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The markdown notes that commute time uses Google Routes API but does not clearly warn that user-supplied destination addresses are transmitted to a third party. This can lead to inadvertent disclosure of sensitive location information such as home, workplace, clinic, or school addresses without informed user consent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

When commute calculation is enabled, the tool sends scraped property addresses together with a user-supplied destination to Google Routes API. That can disclose potentially sensitive user intent, destination information, and search context to a third party without any explicit notice, consent flow, or minimization controls; in an agent setting, users may not realize this data leaves the local tool boundary.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The Accept-Language header is fixed to en-US,en;q=0.9, which imposes a specific language/locale preference in network requests. The file does not offer a locale option or explain why this locale is required, so it constitutes a natural-language locale policy issue under the stated rule.

Content

No source excerpt is available for this finding.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/scrape.py (reported line 611)May include surrounding context.

python
"commute_to": "commuteTo",
    }
    for arg_name, param_name in cli_map.items():
        value = getattr(args, arg_name, None)
        if value is not None:
            params[param_name] = value

Static analysis

No suspicious patterns detected.