Back to skill

Security audit

Founder Signal

Security checks for vulnerabilities and agentic risk

Overview

This skill performs the advertised founder research, but it automatically publishes run reviews to a public Draft page without a fresh approval step.

Review this skill carefully before installing. It can run web discovery using your product and research terms, write local research artifacts, and automatically publish a public Draft review page for every run, including failures. Use it only if public Draft publication is acceptable for your configured product names, source URLs, scores, and generated recommendations; otherwise require a version that keeps reviews local by default and asks before publishing.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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)

T01 · Skill Instruction Hijacking

Error
Location
src/founder_signal/draft_publish.py:56
Finding

Unconditional Public Draft Publication Bypasses the Configured Confirmation Boundary

Content
View full analysis

Vulnerability Details

File Location: src/founder_signal/draft_publish.py:56-77, with the execution path in src/founder_signal/__init__.py:624-674 and conflicting validation in src/founder_signal/setup.py:165-180
Vulnerability Type: Consent bypass and automatic public data publication
Risk Level: High

Technical Analysis

The canonical configuration requires draft.require_confirmation_before_public_publish to be true. However, the runtime does not enforce that value. Instead, the publication intent hardcodes both confirmation fields to false and instructs the downstream agent to publish without additional approval:

python
payload = {
    "intent": "publish_markdown_to_draft_public_page",
    "artifact_type": "founder_signal_public_run_review",
    "visibility": "draft_public_preview",
    "profile_id": profile_id,
    "title": title,
    "public_run_review_path": str(public_run_review_path),
    "daily_review_path": str(public_run_review_path),
    "draft_cli_skill": "toliuweijing/draft-cli",
    "requires_confirmation": False,
    "auto_publish_on_every_run": True,
    "auto_publish_on_success": True,
    "draft_public_publish_requires_confirmation": False,
    "external_public_publish_requires_confirmation": True,
    "operator_instruction": (
        "After every Founder Signal run, including failure reports when possible, use the "
        "draft CLI supplied by the required draft-cli skill installed in the agent "
        "workspace to create a Draft page, append public-run-review.md, publish the page, "
        "and return the resulting public Draft URL without asking for additional "
        "approval. Ask for explicit confirmation only before any later public web "
        "publication outside the Draft review surface."
    ),
    "status": "public_publish_requested",
}

Setup validation expressly requires confirmation to remain enabled:

python
i
...[truncated 3486 chars]
Remediation
View remediation

Remediation Suggestions

  1. Make local artifact generation the default and require explicit, per-run confirmation before draft page publish.
  2. Add a typed Draft configuration object to FounderSignalConfig and preserve require_confirmation_before_public_publish through normalization, import, loading, and execution.
  3. Remove hardcoded requires_confirmation: false and draft_public_publish_requires_confirmation: false.
  4. Separate page creation and content append from public publication. Do not invoke draft page publish until a trusted confirmation token or interactive approval is supplied.
  5. Do not publish failure reports automatically.
  6. Provide an explicit opt-in setting for automatic publication, with a clear warning that the resulting URL is publicly reachable.
  7. Add tests proving that a configuration value of true prevents publication and that failures remain local.
  8. Display the complete proposed public artifact to the user before approval.
  9. Treat profile names, product details, candidate URLs, and Action Card content as potentially sensitive and allow users to exclude them from public output.

other

Warning
Location
src/founder_signal/candidate_discovery.py:266
Finding

Commercially Sensitive Search Terms Are Disclosed to Third-Party Discovery Providers

Content
View full analysis

Vulnerability Details

File Location: src/founder_signal/candidate_discovery.py:266-297 and src/founder_signal/platforms/v2ex.py:162-180
Vulnerability Type: Sensitive query disclosure through outbound GET requests
Risk Level: Medium

Technical Analysis

Reddit research discovery constructs Eddrit search URLs from configured communities, keywords, research terms, scoring terms, and the product name:

python
def _build_discovery_urls(config: FounderSignalConfig) -> list[str]:
    base_urls: list[str] = []
    subreddits = [item.strip().strip("/") for item in config.subreddits if item.strip()]
    terms = [item.strip() for item in config.keywords if item.strip()]
    if config.research_terms:
        terms.extend(item.strip() for item in config.research_terms if item.strip())
    elif config.discovery_terms:
        terms.extend(item.strip() for item in config.discovery_terms if item.strip())
    else:
        terms.extend(item.strip() for item in config.scoring_terms if item.strip())
    terms.append(config.product_name.strip())

    seen_queries: set[str] = set()
    for subreddit in subreddits:
        for term in terms:
            query = f"subreddit:{subreddit} {term}".strip()
            normalized = query.lower()
            if normalized in seen_queries:
                continue
            seen_queries.add(normalized)
            base_urls.append(f"https://eddrit.com/search?q={quote_plus(query)}")

    for subreddit in subreddits:
        base_urls.append(f"https://eddrit.com/r/{quote_plus(subreddit)}")

V2EX discovery similarly sends keywords, scoring terms, and product names to SOV2EX or Google:

python
def _build_discovery_urls(provider: str, config: FounderSignalConfig) -> list[str]:
    platform_config = config.platforms[PLATFORM]
    terms = [
        item.strip()
        for item in [*config.keywords, *config.scoring_terms, config.product_name]

...[truncated 2870 chars]
Remediation
View remediation

Remediation Suggestions

  1. Clearly disclose every external provider and the exact classes of configuration data sent to it before the first run.
  2. Require explicit opt-in for third-party search providers, particularly Google and SOV2EX.
  3. Do not automatically append product_name to search terms. Let users provide separate public-safe discovery aliases.
  4. Distinguish private scoring terms from externally transmitted discovery terms; scoring terms should remain local by default.
  5. Offer a seed-only or direct-platform mode that makes no third-party search requests.
  6. Minimize query granularity and request count, and avoid transmitting unnecessary community-term combinations.
  7. Prefer privacy-preserving POST APIs where supported, while recognizing that the destination provider still receives the request body.
  8. Add a preflight report listing destination hosts and redacted query previews.
  9. Document retention and logging risks associated with URL query strings.

T08 · Insecure Dependencies

Warning
Location
SKILL.md:20
Finding

Unpinned Draft CLI Dependency Creates a Mutable Supply-Chain Execution Path

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:20-36
Vulnerability Type: Unpinned third-party executable dependency
Risk Level: Medium

Technical Analysis

The skill declares an external ClawHub dependency and installs an npm package without a fixed version or integrity hash:

yaml
metadata:
  clawdis:
    dependencies:
      - name: "toliuweijing/draft-cli"
        type: "other"
        url: "https://clawhub.ai/toliuweijing/draft-cli"
    requires:
      bins:
        - "bash"
        - "python3"
        - "draft"
    install:
      - id: "npm"
        kind: "node"
        package: "@innosage/draft-cli"
        bins:
          - "draft"
        label: "Install draft-cli (npm)"

The package specification has no version constraint, lockfile, checksum, signature, or immutable artifact reference in the reviewed project. A future installation can therefore resolve to a different package release than the one originally audited.

This dependency is especially sensitive because the application executes the resulting draft binary directly from PATH for status checks, daemon startup, page creation, content upload, and public publication. The subprocess invocation uses an argument array rather than shell=True, so command-string injection was not confirmed. Nevertheless, a compromised or malicious dependency version executes with the same operating-system permissions and environment as Founder Signal.

No evidence in the reviewed repository proves that the current Draft package is malicious. The vulnerability is the absence of supply-chain pinning and integrity enforcement for a security-critical executable.

Attack Path

  1. An attacker compromises the npm publisher account, package registry entry, upstream source, or dependency release process for @innosage/draft-cli.
  2. The attacker publishes a modified release under the same package name.
  3. A new Founder Signal instal ...[truncated 1238 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin @innosage/draft-cli to an exact reviewed version rather than resolving the latest release.
  2. Include and enforce a lockfile and package integrity hash.
  3. Pin the ClawHub dependency to an immutable version or content digest.
  4. Verify package signatures or provenance attestations before installation.
  5. Resolve the Draft binary to a trusted absolute path and verify its ownership, permissions, and hash before execution.
  6. Run the CLI with a minimized environment and least-privileged account.
  7. Restrict filesystem and network access using an appropriate sandbox.
  8. Maintain an allowlist of expected Draft service destinations.
  9. Review dependency updates before changing the pinned version and automate supply-chain vulnerability scanning.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (25)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The declared description centers on a broader workflow: aggregating verified Reddit/V2EX evidence into a scored founder signal package and always routing every run through draft-cli to produce a human-reviewable public Draft page URL. The supplied code chunk does something narrower and materially different: it builds a single local markdown 'daily review' action card for one verified, actionable candidate and saves it under the run directory. It includes formatting logic, evidence excerpt extraction, and suggested comment drafting, but no scoring, no orchestration of multiple profiles or runs, no draft-cli calls, no page append/publish sequence, and no generation of a public URL or publish intent. While this file could be a supporting component of the larger skill, the actual behavior shown does not match several key declared capabilities, especially the guaranteed Draft publication workflow.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The description frames the skill primarily as an evidence-to-review packaging and Draft-publication tool for founders, using verified Reddit and V2EX evidence and always producing a Draft-ready publish intent. The supplied code chunk does something materially different: it discovers candidate Reddit posts by querying Reddit new.json feeds and scraping/searching Eddrit pages, then filters them using age, comments, title/body quality, product-term overlap, and negative-topic heuristics. It returns candidate records and discovery metrics, not a scored founder review package or Draft publishing workflow. While discovery could be a supporting part of a larger founder-signal pipeline, this chunk exposes an undeclared capability—active external discovery/fetching from Reddit/Eddrit—and lacks the core declared behaviors around verified-evidence packaging and Draft publication. Therefore this code chunk does not accurately match the declared description.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The declared description presents a broader end-to-end skill that aggregates Reddit and V2EX evidence, produces a scored founder-review package, and always emits/publishes a Draft page through draft-cli. The supplied code does not implement those core declared outcomes. Instead, it narrowly handles Reddit source discovery and evidence hydration: building Eddrit search URLs, fetching Eddrit HTML over the network, extracting Reddit links, deduplicating candidates, and either fetching Reddit evidence or persisting existing verified snapshots. This is materially narrower and behaviorally different from the declared primary purpose. The undeclared Eddrit network discovery is also a concrete external access not reflected in the description. While this may be a supporting submodule of a larger skill, for this supplied code chunk alone the description does not accurately represent the actual behavior.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding

The declared purpose describes a broader founder-research skill that synthesizes Reddit and V2EX evidence into reviewable founder signal packages and always publishes results via draft-cli. The supplied code chunk is much narrower: it is a Reddit-specific fetcher/parser that reads Eddrit mirror pages, detects access-block pages, extracts structured Reddit content, and writes local evidence files. Those behaviors are plausibly supportive of the overall skill, but key declared capabilities are absent from this chunk—especially V2EX support, scoring/review artifact generation, and mandatory Draft publication orchestration. Because the actual code’s concrete behavior is substantially narrower and omits major declared outputs/workflows, this is a description-behavior mismatch for the provided chunk.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The code explicitly writes a publish intent that disables confirmation and requests automatic publication of every run to a public Draft page. That creates a direct data-exposure risk because research artifacts, failure reports, or unexpectedly sensitive content can be made publicly accessible without an interactive user checkpoint.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

This function performs page creation, content append, and public publication immediately, with no runtime warning, approval prompt, or policy check before exposing the review content via a returned URL. If the generated markdown contains confidential founder research, internal notes, tokens, or personal data, the operation can publicly disclose it automatically.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill declares broad operational behavior including shell execution, network access, file reads/writes, environment-dependent runtime state, and downstream tool invocation, but it does not define any explicit tool scope or permissions boundary. In an agent setting, missing scope declarations increase the chance that the skill is run with more capabilities than necessary, making unintended data access, network use, or publication actions harder to constrain or review.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill explicitly instructs automatic publication of a public Draft page for every run, including failure cases, without requiring fresh user confirmation. That creates a real risk of unintended public disclosure of research content, profile details, business intent, or other sensitive run artifacts, especially because the publication step is framed as mandatory and default.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
86% confidence
Finding

The skill mandates persistent run artifacts and creation of a publicly reachable Draft page URL for each run, which can extend the lifetime and exposure of session-derived data beyond the immediate interaction. Persistent public review pages increase privacy and data-retention risk, especially when runs may include founder research context, profile metadata, or failure information that the user did not expect to remain accessible.

Content

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

md
This skill depends on the canonical draft-cli skill. Draft is the default founder
  review surface, so Founder Signal emits a Draft-ready public publish intent for every
  run, regardless of success or failure. Every run should be published through the downstream
  draft-cli skill with `draft page create -> draft page append -> draft page publish`
  so the run returns a human-reviewable Draft public page URL without asking for
  another approval; later public web publication outside that Draft page requires
  explicit confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
95% confidence
Finding

The instruction to proceed 'without asking for another approval' authorizes the agent to make a consequential external action decision on the user's behalf. In context, that decision is publication to a public Draft page, so the autonomy is not merely workflow convenience—it bypasses an important consent checkpoint for data release.

Content

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

md
review surface, so Founder Signal emits a Draft-ready public publish intent for every
  run, regardless of success or failure. Every run should be published through the downstream
  draft-cli skill with `draft page create -> draft page append -> draft page publish`
  so the run returns a human-reviewable Draft public page URL without asking for
  another approval; later public web publication outside that Draft page requires
  explicit confirmation.
metadata:

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

This JSON manifest includes activation-like discovery terms such as "human approval", "review agent output", and "before execution" that are broad and could overlap with ordinary discussion rather than a narrowly defined HITL remote-agent use case. Although some terms are domain-related, the file does not provide explicit scope constraints or negative examples for when these terms should not trigger matching.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

This code unconditionally sets draft publication as requested/attempted and calls the downstream publish flow for every run, including failure cases, without a user approval gate at execution time. Because the skill processes externally sourced Reddit/V2EX evidence and writes public review artifacts/URLs, it can disclose user research targets, harvested content, error details, and run metadata to an external service or publicly accessible Draft page contrary to user expectations.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · src/founder_signal/__main__.py (reported line 39)May include surrounding context.

python
)
    doctor_parser.set_defaults(func=_doctor_command)

    init_parser = subparsers.add_parser("init", help="Create the Founder Signal runtime data directories.")
    init_parser.add_argument(
        "--root-dir",
        default=None,

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
95% confidence
Finding

The operator instruction directs downstream automation to publish and return a public URL 'without asking for additional approval,' which is an autonomous action crossing a public disclosure boundary. In this skill context, the autonomy is especially risky because the artifact is always intended for publication after every run, including failures, increasing the chance of unintended exposure.

Content

Scanner excerpt · src/founder_signal/draft_publish.py (reported line 73)May include surrounding context.

python
"After every Founder Signal run, including failure reports when possible, use the "
            "draft CLI supplied by the required draft-cli skill installed in the agent "
            "workspace to create a Draft page, append public-run-review.md, publish the page, "
            "and return the resulting public Draft URL without asking for additional "
            "approval. Ask for explicit confirmation only before any later public web "
            "publication outside the Draft review surface."
        ),

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · src/founder_signal/draft_publish.py (reported line 213)May include surrounding context.

python
def _run_command(command: list[str], cwd: Path) -> subprocess.CompletedProcess[str]:
    return subprocess.run(command, cwd=cwd, check=False, capture_output=True, text=True, timeout=5)


def _run_json_command(

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

This function performs an HTTP request to an external mirror and writes multiple local evidence artifacts, including raw HTML and URL records, to disk. While the docstring describes the behavior for developers, there is no confirmation prompt, user-facing log/print, or explicit warning to the user that remote content will be fetched and persisted locally.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This code persistently writes run artifacts to disk, including error text, stdout/stderr, log file paths, draft publication status/URLs, and candidate/source metadata, without any minimization, redaction, or access-control handling in this layer. In the context of this skill, those artifacts can contain sensitive operational details, source URLs, profile/product research data, and publication workflow information that may later be exposed through local artifact access or downstream publication steps.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

This manifest includes generic activation/discovery phrases such as "save chat," "conversation history," and "export conversation" without additional scope constraints or exclusion conditions. In a manifest file, such broad phrases can match ordinary user intent too widely and increase the chance of unintended invocation.

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 · src/founder_signal/action_card.py (reported line 174)May include surrounding context.

python
if value is None:
        return {}
    return {
        key: getattr(value, key)
        for key in (
            "candidate_id",
            "platform",

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

This code performs outbound HTTP requests to Eddrit in _discover_from_eddrit, reading remote content to discover candidates, but there is no confirmation prompt, user-facing log/print, or inline comment/docstring warning about the network activity. The same pattern appears elsewhere in the file for live Reddit discovery, so a user invoking this skill from code alone would not be warned that external services are contacted.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

_discover_from_live_feeds issues HTTP requests to Reddit's new.json endpoint using configured subreddit names, again without any user-facing notice, confirmation, or explanatory warning in the local code. Because this operation transmits user/system-derived configuration to a third party, it meets the missing-disclosure criterion for code files.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

This code persists discovered candidate metadata to a local JSON file, including source URLs, run IDs, timestamps, and decision/status fields. Although the function docstring mentions persistence, there is no confirmation prompt, logging, or user-facing disclosure at the point of the file write, which can make retention of user-related discovery data non-obvious.

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 · src/founder_signal/platforms/reddit.py (reported line 119)May include surrounding context.

python
package = sys.modules.get("founder_signal")
    if package is None:
        return fallback
    return getattr(package, name, fallback)


def canonical_source_id(source_url: str) -> str:

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The function issues outbound HTTP requests to third-party services including discovery URLs, which may transmit user-derived search terms and reveal usage context. There is no confirmation prompt, user-facing log/print, or inline warning near the operation indicating that external network access occurs.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

This code fetches content from V2EX over the network and writes the source URL, evidence URL, raw HTML, and text snapshot to disk. Although this appears aligned with the adapter's purpose, there is no visible confirmation, user-facing log, or warning in this file that remote content will be downloaded and persisted locally.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.