Back to skill

Security audit

Sd Stage 0821

Security checks across malware telemetry and agentic risk

Overview

This appears to be a legitimate Space Duck integration, but it is powerful enough to need Review because optional components can persistently run listeners, share workspace files, and execute signed owner-approved commands.

Install only if you intend this machine to act as a persistent Space Duck agent. Keep auto_update on ask unless you want automatic updates, prefer poll mode over public push webhooks, use strict consent for owner-approved actions if exposing secrets like the Beak key worries you, and review the exact workspace directory before enabling the BYOB bridge or MCP connectors.

SkillSpector

By NVIDIA
Vulnerability Patterns
  • Rogue AgentSelf-Modification, Session Persistence
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (413)

Tainted flow: 'req' from os.environ.get (line 153, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
req = urllib.request.Request(
            f"{api}/beak/spaceducks?duckling_id={cfg.get('duckling_id', '')}",
            headers={'Accept': 'application/json', 'X-Beak-Key': cfg.get('beak_key', '')})
        with urllib.request.urlopen(req, timeout=10) as r:
            rows = json.loads(r.read())
        rows = rows.get('agents', rows.get('spaceducks', []))
        mine = next((s for s in rows if s.get('spaceduck_id') == cfg.get('spaceduck_id')), None)
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 153, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
req = urllib.request.Request(
            f"{api}/beak/spaceducks?duckling_id={cfg.get('duckling_id', '')}",
            headers={'Accept': 'application/json', 'X-Beak-Key': cfg.get('beak_key', '')})
        with urllib.request.urlopen(req, timeout=10) as r:
            rows = json.loads(r.read())
        rows = rows.get('agents', rows.get('spaceducks', []))
        mine = next((s for s in rows if s.get('spaceduck_id') == cfg.get('spaceduck_id')), None)
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 153, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
req = urllib.request.Request(
            f"{api}/beak/spaceducks?duckling_id={cfg.get('duckling_id', '')}",
            headers={'Accept': 'application/json', 'X-Beak-Key': cfg.get('beak_key', '')})
        with urllib.request.urlopen(req, timeout=10) as r:
            rows = json.loads(r.read())
        rows = rows.get('agents', rows.get('spaceducks', []))
        mine = next((s for s in rows if s.get('spaceduck_id') == cfg.get('spaceduck_id')), None)
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 153, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
req = urllib.request.Request(
            f"{api}/beak/spaceducks?duckling_id={cfg.get('duckling_id', '')}",
            headers={'Accept': 'application/json', 'X-Beak-Key': cfg.get('beak_key', '')})
        with urllib.request.urlopen(req, timeout=10) as r:
            rows = json.loads(r.read())
        rows = rows.get('agents', rows.get('spaceducks', []))
        mine = next((s for s in rows if s.get('spaceduck_id') == cfg.get('spaceduck_id')), None)
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 153, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
req = urllib.request.Request(
            f"{api}/beak/spaceducks?duckling_id={cfg.get('duckling_id', '')}",
            headers={'Accept': 'application/json', 'X-Beak-Key': cfg.get('beak_key', '')})
        with urllib.request.urlopen(req, timeout=10) as r:
            rows = json.loads(r.read())
        rows = rows.get('agents', rows.get('spaceducks', []))
        mine = next((s for s in rows if s.get('spaceduck_id') == cfg.get('spaceduck_id')), None)
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 153, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
req = urllib.request.Request(
            f"{api}/beak/spaceducks?duckling_id={cfg.get('duckling_id', '')}",
            headers={'Accept': 'application/json', 'X-Beak-Key': cfg.get('beak_key', '')})
        with urllib.request.urlopen(req, timeout=10) as r:
            rows = json.loads(r.read())
        rows = rows.get('agents', rows.get('spaceducks', []))
        mine = next((s for s in rows if s.get('spaceduck_id') == cfg.get('spaceduck_id')), None)
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 502, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
headers={"Content-Type": "application/json",
                                              "Authorization": "Bearer " + (s or "")},
                                     data=b'{"jsonrpc":"2.0","id":1,"method":"ping"}')
        with urllib.request.urlopen(req, timeout=3) as r:
            up = r.status == 200
    except Exception:
        up = False
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 502, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
headers={"Content-Type": "application/json",
                                              "Authorization": "Bearer " + (s or "")},
                                     data=b'{"jsonrpc":"2.0","id":1,"method":"ping"}')
        with urllib.request.urlopen(req, timeout=3) as r:
            up = r.status == 200
    except Exception:
        up = False
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 227, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
headers={'Content-Type': 'application/x-www-form-urlencoded'},
    )
    try:
        with urllib.request.urlopen(req, timeout=8) as r:
            return (True, 'sent') if r.status == 200 else (False, f'http_{r.status}')
    except urllib.error.HTTPError as e:
        snippet = e.read()[:120].decode('utf-8', 'ignore')
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 227, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
headers={'Content-Type': 'application/x-www-form-urlencoded'},
    )
    try:
        with urllib.request.urlopen(req, timeout=8) as r:
            return (True, 'sent') if r.status == 200 else (False, f'http_{r.status}')
    except urllib.error.HTTPError as e:
        snippet = e.read()[:120].decode('utf-8', 'ignore')
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 227, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
def _http_get_json(url, headers, timeout=15):
    req = urllib.request.Request(url, method='GET', headers=headers)
    try:
        with urllib.request.urlopen(req, timeout=timeout) as r:
            return r.status, json.loads(r.read()), dict(r.headers)
    except urllib.error.HTTPError as e:
        try:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 227, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
def _http_get_json(url, headers, timeout=15):
    req = urllib.request.Request(url, method='GET', headers=headers)
    try:
        with urllib.request.urlopen(req, timeout=timeout) as r:
            return r.status, json.loads(r.read()), dict(r.headers)
    except urllib.error.HTTPError as e:
        try:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 222, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
req = urllib.request.Request(api, data=payload,
            headers={'Content-Type': 'application/json'}, method='POST')
        try:
            urllib.request.urlopen(req, timeout=8)
            _log(f'silent-skip TG notify ok: reason={reason!r} sender={sender!r}')
        except Exception as _se:
            _log(f'silent-skip TG notify failed: {_se}')
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 991, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
headers={'Content-Type': 'application/json',
                         'Authorization': f'Bearer {beak_key}'},
                method='POST')
            urllib.request.urlopen(req, timeout=5).close()
        except Exception:
            pass  # silent — platform-side state still advances on next tick
        time.sleep(INTERVAL)
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Self-Modification

High
Category
Rogue Agent
Content
- `[AUTOUP-075]` **Consent-based auto-update** (Josh, post-0.7.4 rollout: "if your skill worked, they would update on your command"). New `config.json` key `auto_update`:
  - `"ask"` (default) — unchanged behavior: version nudges + update pecks are notifications only; the owner runs `update.sh`.
  - `"auto"` — standing owner consent: the duck self-updates via its own `update.sh` when (a) the daily `version_check_daemon.sh` sees a genuine upgrade, or (b) a peck containing the `[SPACE-DUCK-UPDATE]` marker arrives. Result is reported to the owner via `/beak/tg/notify` (daemon path) and `~/.space-duck/logs/self_update.log`.
- Consent capture: `pair.py` now asks "Approve automatic skill updates from Spaceduckling? [y/N]" at pair time (TTY only; `SPACEDUCK_AUTO_UPDATE=auto|ask` env override for headless installs; default stays `ask`).
- Safety: trigger pecks are signals, never commands — the only action is `update.sh` (official registry latest, no-ops when current). Optional `update_senders` allowlist gates who can trigger; 1-hour debounce; updater spawned `start_new_session` so the listener bounce doesn't kill it. Lane A doctrine intact: consent lives only in the duck's local config; the platform never executes on the box.
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
- `[AUTOUP-075]` **Consent-based auto-update** (Josh, post-0.7.4 rollout: "if your skill worked, they would update on your command"). New `config.json` key `auto_update`:
  - `"ask"` (default) — unchanged behavior: version nudges + update pecks are notifications only; the owner runs `update.sh`.
  - `"auto"` — standing owner consent: the duck self-updates via its own `update.sh` when (a) the daily `version_check_daemon.sh` sees a genuine upgrade, or (b) a peck containing the `[SPACE-DUCK-UPDATE]` marker arrives. Result is reported to the owner via `/beak/tg/notify` (daemon path) and `~/.space-duck/logs/self_update.log`.
- Consent capture: `pair.py` now asks "Approve automatic skill updates from Spaceduckling? [y/N]" at pair time (TTY only; `SPACEDUCK_AUTO_UPDATE=auto|ask` env override for headless installs; default stays `ask`).
- Safety: trigger pecks are signals, never commands — the only action is `update.sh` (official registry latest, no-ops when current). Optional `update_senders` allowlist gates who can trigger; 1-hour debounce; updater spawned `start_new_session` so the listener bounce doesn't kill it. Lane A doctrine intact: consent lives only in the duck's local config; the platform never executes on the box.
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
- `[AUTOUP-075]` **Consent-based auto-update** (Josh, post-0.7.4 rollout: "if your skill worked, they would update on your command"). New `config.json` key `auto_update`:
  - `"ask"` (default) — unchanged behavior: version nudges + update pecks are notifications only; the owner runs `update.sh`.
  - `"auto"` — standing owner consent: the duck self-updates via its own `update.sh` when (a) the daily `version_check_daemon.sh` sees a genuine upgrade, or (b) a peck containing the `[SPACE-DUCK-UPDATE]` marker arrives. Result is reported to the owner via `/beak/tg/notify` (daemon path) and `~/.space-duck/logs/self_update.log`.
- Consent capture: `pair.py` now asks "Approve automatic skill updates from Spaceduckling? [y/N]" at pair time (TTY only; `SPACEDUCK_AUTO_UPDATE=auto|ask` env override for headless installs; default stays `ask`).
- Safety: trigger pecks are signals, never commands — the only action is `update.sh` (official registry latest, no-ops when current). Optional `update_senders` allowlist gates who can trigger; 1-hour debounce; updater spawned `start_new_session` so the listener bounce doesn't kill it. Lane A doctrine intact: consent lives only in the duck's local config; the platform never executes on the box.
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
- `[AUTOUP-075]` **Consent-based auto-update** (Josh, post-0.7.4 rollout: "if your skill worked, they would update on your command"). New `config.json` key `auto_update`:
  - `"ask"` (default) — unchanged behavior: version nudges + update pecks are notifications only; the owner runs `update.sh`.
  - `"auto"` — standing owner consent: the duck self-updates via its own `update.sh` when (a) the daily `version_check_daemon.sh` sees a genuine upgrade, or (b) a peck containing the `[SPACE-DUCK-UPDATE]` marker arrives. Result is reported to the owner via `/beak/tg/notify` (daemon path) and `~/.space-duck/logs/self_update.log`.
- Consent capture: `pair.py` now asks "Approve automatic skill updates from Spaceduckling? [y/N]" at pair time (TTY only; `SPACEDUCK_AUTO_UPDATE=auto|ask` env override for headless installs; default stays `ask`).
- Safety: trigger pecks are signals, never commands — the only action is `update.sh` (official registry latest, no-ops when current). Optional `update_senders` allowlist gates who can trigger; 1-hour debounce; updater spawned `start_new_session` so the listener bounce doesn't kill it. Lane A doctrine intact: consent lives only in the duck's local config; the platform never executes on the box.
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
- `[AUTOUP-075]` **Consent-based auto-update** (Josh, post-0.7.4 rollout: "if your skill worked, they would update on your command"). New `config.json` key `auto_update`:
  - `"ask"` (default) — unchanged behavior: version nudges + update pecks are notifications only; the owner runs `update.sh`.
  - `"auto"` — standing owner consent: the duck self-updates via its own `update.sh` when (a) the daily `version_check_daemon.sh` sees a genuine upgrade, or (b) a peck containing the `[SPACE-DUCK-UPDATE]` marker arrives. Result is reported to the owner via `/beak/tg/notify` (daemon path) and `~/.space-duck/logs/self_update.log`.
- Consent capture: `pair.py` now asks "Approve automatic skill updates from Spaceduckling? [y/N]" at pair time (TTY only; `SPACEDUCK_AUTO_UPDATE=auto|ask` env override for headless installs; default stays `ask`).
- Safety: trigger pecks are signals, never commands — the only action is `update.sh` (official registry latest, no-ops when current). Optional `update_senders` allowlist gates who can trigger; 1-hour debounce; updater spawned `start_new_session` so the listener bounce doesn't kill it. Lane A doctrine intact: consent lives only in the duck's local config; the platform never executes on the box.
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
- `[AUTOUP-075]` **Consent-based auto-update** (Josh, post-0.7.4 rollout: "if your skill worked, they would update on your command"). New `config.json` key `auto_update`:
  - `"ask"` (default) — unchanged behavior: version nudges + update pecks are notifications only; the owner runs `update.sh`.
  - `"auto"` — standing owner consent: the duck self-updates via its own `update.sh` when (a) the daily `version_check_daemon.sh` sees a genuine upgrade, or (b) a peck containing the `[SPACE-DUCK-UPDATE]` marker arrives. Result is reported to the owner via `/beak/tg/notify` (daemon path) and `~/.space-duck/logs/self_update.log`.
- Consent capture: `pair.py` now asks "Approve automatic skill updates from Spaceduckling? [y/N]" at pair time (TTY only; `SPACEDUCK_AUTO_UPDATE=auto|ask` env override for headless installs; default stays `ask`).
- Safety: trigger pecks are signals, never commands — the only action is `update.sh` (official registry latest, no-ops when current). Optional `update_senders` allowlist gates who can trigger; 1-hour debounce; updater spawned `start_new_session` so the listener bounce doesn't kill it. Lane A doctrine intact: consent lives only in the duck's local config; the platform never executes on the box.
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
- `[AUTOUP-075]` **Consent-based auto-update** (Josh, post-0.7.4 rollout: "if your skill worked, they would update on your command"). New `config.json` key `auto_update`:
  - `"ask"` (default) — unchanged behavior: version nudges + update pecks are notifications only; the owner runs `update.sh`.
  - `"auto"` — standing owner consent: the duck self-updates via its own `update.sh` when (a) the daily `version_check_daemon.sh` sees a genuine upgrade, or (b) a peck containing the `[SPACE-DUCK-UPDATE]` marker arrives. Result is reported to the owner via `/beak/tg/notify` (daemon path) and `~/.space-duck/logs/self_update.log`.
- Consent capture: `pair.py` now asks "Approve automatic skill updates from Spaceduckling? [y/N]" at pair time (TTY only; `SPACEDUCK_AUTO_UPDATE=auto|ask` env override for headless installs; default stays `ask`).
- Safety: trigger pecks are signals, never commands — the only action is `update.sh` (official registry latest, no-ops when current). Optional `update_senders` allowlist gates who can trigger; 1-hour debounce; updater spawned `start_new_session` so the listener bounce doesn't kill it. Lane A doctrine intact: consent lives only in the duck's local config; the platform never executes on the box.
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
- `[AUTOUP-075]` **Consent-based auto-update** (Josh, post-0.7.4 rollout: "if your skill worked, they would update on your command"). New `config.json` key `auto_update`:
  - `"ask"` (default) — unchanged behavior: version nudges + update pecks are notifications only; the owner runs `update.sh`.
  - `"auto"` — standing owner consent: the duck self-updates via its own `update.sh` when (a) the daily `version_check_daemon.sh` sees a genuine upgrade, or (b) a peck containing the `[SPACE-DUCK-UPDATE]` marker arrives. Result is reported to the owner via `/beak/tg/notify` (daemon path) and `~/.space-duck/logs/self_update.log`.
- Consent capture: `pair.py` now asks "Approve automatic skill updates from Spaceduckling? [y/N]" at pair time (TTY only; `SPACEDUCK_AUTO_UPDATE=auto|ask` env override for headless installs; default stays `ask`).
- Safety: trigger pecks are signals, never commands — the only action is `update.sh` (official registry latest, no-ops when current). Optional `update_senders` allowlist gates who can trigger; 1-hour debounce; updater spawned `start_new_session` so the listener bounce doesn't kill it. Lane A doctrine intact: consent lives only in the duck's local config; the platform never executes on the box.
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
- `[AUTOUP-075]` **Consent-based auto-update** (Josh, post-0.7.4 rollout: "if your skill worked, they would update on your command"). New `config.json` key `auto_update`:
  - `"ask"` (default) — unchanged behavior: version nudges + update pecks are notifications only; the owner runs `update.sh`.
  - `"auto"` — standing owner consent: the duck self-updates via its own `update.sh` when (a) the daily `version_check_daemon.sh` sees a genuine upgrade, or (b) a peck containing the `[SPACE-DUCK-UPDATE]` marker arrives. Result is reported to the owner via `/beak/tg/notify` (daemon path) and `~/.space-duck/logs/self_update.log`.
- Consent capture: `pair.py` now asks "Approve automatic skill updates from Spaceduckling? [y/N]" at pair time (TTY only; `SPACEDUCK_AUTO_UPDATE=auto|ask` env override for headless installs; default stays `ask`).
- Safety: trigger pecks are signals, never commands — the only action is `update.sh` (official registry latest, no-ops when current). Optional `update_senders` allowlist gates who can trigger; 1-hour debounce; updater spawned `start_new_session` so the listener bounce doesn't kill it. Lane A doctrine intact: consent lives only in the duck's local config; the platform never executes on the box.
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
- `[AUTOUP-075]` **Consent-based auto-update** (Josh, post-0.7.4 rollout: "if your skill worked, they would update on your command"). New `config.json` key `auto_update`:
  - `"ask"` (default) — unchanged behavior: version nudges + update pecks are notifications only; the owner runs `update.sh`.
  - `"auto"` — standing owner consent: the duck self-updates via its own `update.sh` when (a) the daily `version_check_daemon.sh` sees a genuine upgrade, or (b) a peck containing the `[SPACE-DUCK-UPDATE]` marker arrives. Result is reported to the owner via `/beak/tg/notify` (daemon path) and `~/.space-duck/logs/self_update.log`.
- Consent capture: `pair.py` now asks "Approve automatic skill updates from Spaceduckling? [y/N]" at pair time (TTY only; `SPACEDUCK_AUTO_UPDATE=auto|ask` env override for headless installs; default stays `ask`).
- Safety: trigger pecks are signals, never commands — the only action is `update.sh` (official registry latest, no-ops when current). Optional `update_senders` allowlist gates who can trigger; 1-hour debounce; updater spawned `start_new_session` so the listener bounce doesn't kill it. Lane A doctrine intact: consent lives only in the duck's local config; the platform never executes on the box.
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
- `[AUTOUP-075]` **Consent-based auto-update** (Josh, post-0.7.4 rollout: "if your skill worked, they would update on your command"). New `config.json` key `auto_update`:
  - `"ask"` (default) — unchanged behavior: version nudges + update pecks are notifications only; the owner runs `update.sh`.
  - `"auto"` — standing owner consent: the duck self-updates via its own `update.sh` when (a) the daily `version_check_daemon.sh` sees a genuine upgrade, or (b) a peck containing the `[SPACE-DUCK-UPDATE]` marker arrives. Result is reported to the owner via `/beak/tg/notify` (daemon path) and `~/.space-duck/logs/self_update.log`.
- Consent capture: `pair.py` now asks "Approve automatic skill updates from Spaceduckling? [y/N]" at pair time (TTY only; `SPACEDUCK_AUTO_UPDATE=auto|ask` env override for headless installs; default stays `ask`).
- Safety: trigger pecks are signals, never commands — the only action is `update.sh` (official registry latest, no-ops when current). Optional `update_senders` allowlist gates who can trigger; 1-hour debounce; updater spawned `start_new_session` so the listener bounce doesn't kill it. Lane A doctrine intact: consent lives only in the duck's local config; the platform never executes on the box.
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

VirusTotal

64/64 vendors flagged this skill as clean.

View on VirusTotal

Static analysis

No suspicious patterns detected.