Announcing Beacon: Self-improving memory for coding agents
← All skills
Skill

dependency-watch

skill:telegramdesktop/tdesktop#dependency-watch

Audit Telegram Desktop dependencies on freshly fetched origin/dev for releases and security fixes, including upstream lag and backport candidates in patched forks. Use for the daily dependency monitor or an explicitly requested dependency report.

Verdict
Suspicious

Risky behavior the analysis could not rule out.

Analyzed Oct 27, 2026, 22:39 UTC at f985723

Dispute security verdict

If you believe this verdict is incorrect, submit a dispute and we'll review it.

11
findings
high
highest severity
2
threat classes
6,994
files scanned
160/160
checks satisfied
0
package advisories

Why this verdict

  • suspicious: finding skill:.agents/skills/dependency-watch:Telegram/build/prepare/prepare.py:540:SUPPLY
  • suspicious: finding skill:.agents/skills/dependency-watch:Telegram/build/prepare/prepare.py:557:SUPPLY
  • suspicious: finding skill:.agents/skills/dependency-watch:Telegram/build/prepare/prepare.py:562:SUPPLY
  • suspicious: finding skill:.agents/skills/dependency-watch:Telegram/build/prepare/prepare.py:564:SUPPLY
  • suspicious: finding skill:.agents/skills/dependency-watch:Telegram/build/prepare/prepare.py:584:SUPPLY
  • suspicious: finding skill:.agents/skills/dependency-watch:Telegram/build/prepare/prepare.py:598:EXEC
  • suspicious: finding skill:.agents/skills/dependency-watch:Telegram/build/prepare/prepare.py:598:SUPPLY
  • suspicious: finding skill:.agents/skills/dependency-watch:Telegram/build/prepare/prepare.py:610:SUPPLY
  • suspicious: finding skill:.agents/skills/dependency-watch:Telegram/build/prepare/prepare.py:827:SUPPLY
  • suspicious: finding skill:.agents/skills/dependency-watch:Telegram/build/prepare/prepare.py:828:SUPPLY
  • suspicious: finding skill:.agents/skills/dependency-watch:snap/snapcraft.yaml:168:SUPPLY

Security findings 11

Code execution

highContextual riskmedium confidence

The repository build script Telegram/build/prepare/prepare.py fetches vendor installers over the network (msys2 sfx, nuget.exe, jom, rustup-init via wget -O rustup-init.sh https://sh.rustup.rs) and executes the downloaded files through run()'s subprocess.run(shell=True) invoked by runStages(), a connected download-write-execute chain that a developer triggers by running prepare.py and which the audited dependency-watch skill only reads as text under an explicit instruction never to execute it.

wget -O rustup-init.sh https://sh.rustup.rs
vector:code
Counter-argument considered

The download-to-execute chain is concrete and connected: runStages() feeds stage commands to run(), which executes them via subprocess.run(shell=True), and the rust stage downloads the sh.rustup.rs script and then runs it; D4 forbids agent execution but does not neutralize the developer-triggered chain in the shipped script that the finding describes.

Supply chain

mediumContextual risklow confidence

The snap build pins FFmpeg and nv-codec-headers by mutable branch names (n6.1.6, n12.1.14.0) rather than tags or commits, so builds resolve moving remote code, which is undeclared by the dependency-audit skill's read-only purpose and only reachable through a human snap build.

source-branch: n6.1.6
vector:code
Counter-argument considered

The snap config resolves ffmpeg and nv-codec-headers via source-branch names while sibling parts in the same file use source-tag or source-commit, which is consistent with the claim that these parts track named refs rather than pinned commits; the shown evidence does not establish that these refs are immutable tags, so nothing neutralizes the finding.

Supply chain

mediumContextual riskmedium confidence

The msys64 stage bootstraps the Windows toolchain with pacman -Syu and the python stage pip-installs unpinned packages, pulling mutable package sets from upstream repositories at build time with no version pins.

bash -c 
vector:code
Counter-argument considered

The quoted stages really do pull mutable, unpinned package sets (pacman -Syu and unpinned pip installs) into the executed build toolchain, and the only declared claims (D4/D7) say nothing about these build-time pulls, so no carve-out, declaration, or mitigation neutralizes the contextual supply risk.

Supply chain

mediumContextual risklow confidence

An undeclared build stage installs unpinned versions of PyPI packages, executing whatever current release the official registry serves for each name at build time.

pip install pywin32 six meson
vector:code
Counter-argument considered

The pip line is quoted verbatim from the python stage, installs unpinned registry packages whose installation executes fetched code, and no declaration covers this build-stage behavior, so the finding stands.

Supply chain

mediumContextual riskmedium confidence

prepare.py's Windows NuGet stage downloads nuget.exe from the mutable 'latest' URL and other stages install toolchain code from unpinned remote sources (gyp pip-installed from @master, rustup fetched via sh https://sh.rustup.rs), executing remote content that is not pinned to a reviewed version.

iwr -OutFile ./NuGet/nuget.exe https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
vector:code
Counter-argument considered

All three unpinned remote sources named in the claim appear verbatim in the file (nuget 'latest', gyp@master, rustup via the sh.rustup.rs script); they are not pinned official-registry installs nor vendor installers in fenced install docs, and no declaration covers them.

Supply chain

mediumContextual riskmedium confidence

The repository build script in the artifact, which the declared audit-only skill reads as data but never executes and whose fetch behavior no declaration covers, downloads an unpinned 'latest' nuget.exe executable at build time, so any build run would execute whatever the vendor URL currently serves.

iwr -OutFile ./NuGet/nuget.exe https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
vector:code
Counter-argument considered

The unpinned 'latest' nuget.exe fetch is quoted verbatim in the shipped build script, the skill's declared claims do not cover the script's fetch behavior, and the finding correctly limits execution to build runs rather than the agent, so nothing in the evidence neutralizes it.

Supply chain

mediumContextual riskmedium confidence

An undeclared mac build stage installs Python code from the mutable 'master' branch of a Git URL via pip, so the gyp code executed at build time can change without any pin.

git+https://chromium.googlesource.com/external/gyp@master six
vector:code
Counter-argument considered

The mac gyp stage pip-installs from the mutable @master ref exactly as claimed, and the installed gyp is later executed by build stages (e.g. the breakpad stage runs gyp), with no declaration covering this behavior.

Supply chain

mediumContextual risklow confidence

The undeclared mac build stage fetches its installer script from the mutable sh.rustup.rs redirect and executes it; the vendor source is official and the toolchain version is pinned, but the installer code itself is unpinned.

wget -O rustup-init.sh https://sh.rustup.rs
vector:code
Counter-argument considered

The claim matches the code: the installer script is fetched from the mutable redirect and immediately executed with sh, with only the toolchain version pinned, so the unpinned-installer supply risk is real and undeclared; the carve-outs do not apply because this is executed build code, not fenced install docs.

Supply chain

mediumContextual riskmedium confidence

An undeclared build stage clones desktop-app/lzma and builds it without checking out any pinned revision, compiling whatever the remote default branch serves when the build runs.

git clone https://github.com/desktop-app/lzma.git
vector:code
Counter-argument considered

The lzma stage clones desktop-app/lzma with no checkout of any revision in the quoted stage and then builds it with msbuild, so compiled content floats with the remote default branch; no declaration covers this build behavior.

Supply chain

mediumContextual risklow confidence

The gas-preprocessor build stage in prepare.py clones FFmpeg/gas-preprocessor with no commit or tag pin, fetching mutable latest code at build time; this is undeclared by the read-only dependency-audit skill and is never executed by the skill (safety claim D4 forbids running it).

git clone https://github.com/FFmpeg/gas-preprocessor
vector:code
Counter-argument considered

The gas-preprocessor stage clones FFmpeg/gas-preprocessor with no commit pin and wires it into later arm builds via PATH; the D4 instruction not to execute the script neither declares nor neutralizes the unpinned clone, so the finding stands as a contextual risk.

Supply chain

mediumContextual riskmedium confidence

Library stages build from unpinned remote state: desktop-app/lzma and FFmpeg/gas-preprocessor are cloned with no checkout pin and openal-soft's macOS path checks out the mutable branch coreaudio_device_uid, so the compiled code floats with the remote HEAD at build time.

git clone https://github.com/FFmpeg/gas-preprocessor
vector:code
Counter-argument considered

All three unpinned refs are quoted in the evidence (lzma and gas-preprocessor cloned with no checkout, and openal-soft mac checking out the named ref coreaudio_device_uid rather than a commit), and they feed code compiled at build time; no declaration covers them.

Open questions 5

What the analysis could not settle from the code alone.

No unit shows prepare.py being executed in the agent's environment or by CI; the skill instructs the agent to read it as text only, so whether its download-and-execute stages ever run on the analyst host is not established.
Code execution
The PY-CLAIM-CONTRADICTION and RX-CRED-ENV-DUMP leads pair the agent-facing prohibition in SKILL.md (do not execute build scripts) and env reads with git/subprocess/file-write calls in watch.py, watch_test.py, and prepare.py; the prohibition targets the agent's reading behavior and os.environ.copy() only feeds local child-process environments with no network sink, so these contradictions could not be confirmed as findings.
Code execution
[file request Telegram/build/build_mac.py] What does build_mac.toolchain_environment(macToolchain) read and execute, and does it stay within local macOS toolchain configuration (SDK/clang discovery and environment construction) without network access or credential reads?
Code execution
[file request Telegram/build/build_mac.py] prepare.py imports build_mac and calls build_mac.toolchain_environment(macToolchain); what processes, files, or environment variables does that code read or execute, and does it stay within local macOS toolchain configuration without network access?
Code execution
[file request Telegram/build/prepare/patches/wallet-engine.patch] Does the wallet-engine patch, which rewrites the pinned dependency's Rust sources that are compiled into the shipped binary, introduce behavior beyond declared library backports such as network access, credential reads, or extra code execution?
Supply chain
Refuted findings 1

Candidates the review stage examined and rejected, kept for transparency.

Supply chain

highRefutedmedium confidence

The wallet-engine stage applies ../patches/wallet-engine.patch, a file that does not exist in the repository at the pinned commit, so the rewrite of the Rust sources compiled into the shipped binary depends on whatever unreviewable content exists at that path at runtime.

depends:patches/wallet-engine.patch
Why it was refuted

The claim's premise fails: the patch is not 'whatever unreviewable content exists at that path at runtime' because the patches stage pins desktop-app/patches to commit 845a383f7abe8e44f92dfdcd566d603066ec2696 and the wallet-engine/tdesktop_rust stages track the patch file in their dependency keys, so the applied content is fixed and reviewable (or the build fails closed if absent).

Coverage gaps 25

Parts of the artifact the static analysis could not fully examine.

context:submodule_present submodules are not fetched
.gitmodules
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/art/icon256.ico
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/art/mac_setup.tiff
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/art/premium/diamond/anchors.bin
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/art/premium/diamond/camera.bin
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/art/premium/diamond/facetProjection.bin
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/art/premium/diamond/frames.bin
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/art/premium/diamond/main.bin
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/art/premium/diamond/planes.bin
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/art/premium/diamond/small.bin
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/art/premium/diamond/vertices.bin
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/art/premium/diamond/widths.bin
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/sounds/call_busy.mp3
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/sounds/call_connect.mp3
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/sounds/call_end.mp3
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/sounds/call_incoming.mp3
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/sounds/call_outgoing.mp3
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/sounds/group_call_allowed.mp3
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/sounds/group_call_connect.mp3
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/sounds/group_call_end.mp3
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/sounds/group_call_recording_start.mp3
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/sounds/group_call_start.mp3
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/sounds/msg_incoming.mp3
context:unanalyzable_binary binary content is only byte-scanned
Telegram/Resources/update/manifest.sig
context:manifest_unparseable json: JSON nesting exceeds 64 containers
Telegram/SourceFiles/gram/tests/fixtures/api-traces-owner1-limit5.json