Back to skill

Security audit

Speediance Gym Monster

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real Speediance CLI skill, but it needs review because it handles reusable account credentials and has concrete credential-handling weaknesses.

Install only if you are comfortable giving this CLI your Speediance email/password and allowing it to read workout data and create programs on your account. Prefer environment variables or a password manager over config.json/.env, keep token-cache paths private, use dry-run before push, and pin a known release instead of installing a moving latest version.

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 (2)

T09 · Insecure Skill Coding Practices

Warning
Location
internal/api/client.go:88
Finding

Authentication Secrets Can Be Forwarded Across Unrestricted HTTP Redirects

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
internal/cli/config.go:131
Finding

Reusable Account Password Can Be Persisted in Plaintext Configuration

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (46)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared description presents a full-featured Speediance cloud CLI with account authentication, local token caching, outbound HTTPS requests, and data operations on workouts, exercise catalogs, and training programs. The supplied code chunk does none of that. It only defines a Cobra version command that returns build/version metadata in text or JSON. This is a materially different and much narrower behavior than the declared purpose. While a version subcommand could exist within such a tool, this specific chunk does not substantiate the declared capabilities, so the description does not accurately represent what this supplied code chunk actually does.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
83% confidence
Finding

The skill is designed to read credentials from environment variables, config.json, and a local .env file, and to persist a session token on disk. While this is common for CLI tools, it creates a real credential-handling risk: secrets may be exposed through overly broad file access, unsafe file permissions, accidental inclusion in working directories, or theft of the cached token by other local processes.

Content

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

md
- "Speediance cloud API (HTTPS) — authentication, workout history, exercise catalog, program creation"
      files.read:
        - "config.json — optional credential/config file (working directory, or your OS user-config dir)"
        - ".env — optional env file in the working directory; only the documented SPEEDIANCE_* keys are read (parsed to a map, never exported into the process environment)"
        - "token cache — cached session token in your OS user-cache dir by default (non-roaming); path overridable via SPEEDIANCE_TOKEN_CACHE or the token_cache_path config key. A legacy .token.json in the working directory is read once to migrate it."
        - "plan JSON files passed to the push command"
      files.write:

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
Every run saves the full catalog to `--out` (default `library.json`) — `--search`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
Every run saves the full catalog to `--out` (default `library.json`) — `--search`

Credential Access

High
Category
Privilege Escalation
Confidence
80% confidence
Finding

The code automatically reads a .env file from the current working directory, which is attacker-influenceable in agent or automation contexts. Although only SPEEDIANCE_* keys are consumed, a malicious .env can still silently inject service credentials or redirect configuration such as SPEEDIANCE_CONFIG or SPEEDIANCE_TOKEN_CACHE, causing the tool to act under attacker-chosen account context or write sensitive token material to an unsafe path.

Content

Scanner excerpt · internal/config/config.go (reported line 116)May include surrounding context.

go
// a stray or hostile .env could escalate into the tool's runtime. godotenv.Read
	// returns the file as a map and changes no global state, so unknown keys are
	// never applied. The documented feature is preserved: SPEEDIANCE_* values in
	// .env still act as the env layer. A missing/unreadable .env yields a nil map
	// and is a silent no-op.
	dotenv, _ := godotenv.Read()

YARA rule 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [malware]

High
Category
YARA Match
Confidence
75% confidence
Finding

YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Content

Scanner excerpt · internal/config/config_test.go (reported line 332)May include surrounding context.

go
Chdir(dir)
	clearEnv(t)

	// Record the pre-Load state of two real, dangerous knobs so we can prove
	// Load left them exactly as they were rather than adopting the .env values.
	const ldKey, pathProbe = "LD_PRELOAD", "DOTENV_FOREIGN_SENTINEL_SHOULD_BE_IGNORED"
	ldBefore, ldSet := os.LookupEnv(ldKey)

	if err := os.WriteFile(filepath.Join(dir, ".env"), []byte(
		"SPEEDIANCE_EMAIL=ok@b.com\n"+
			"LD_PRELOAD=/tmp/evil.so\n"+
			"PATH=/evil/bin\n"+
			pathProbe+"=injected\n"), 0o600); err != nil {
		t.Fatal(err)
	}

	cfg, err := Load(Options{})
	if err != nil {
		t.Fatal(err)
	}

	// The documented feature still works: SPEEDIANCE_* keys are applied.
	if cfg.Email != "ok@b.com" {
		t.Errorf("email = %q, want ok@b.com (.env SPEEDIANCE_* must still load)", cfg.Email)
	}

	// The guard: none of the foreign keys leaked into the process environment.
	if v, ok := os.LookupEnv(pathProbe); ok {
		t.Errorf("foreign key %s leaked into process env as %q; .env must not mutate the environment", pathPr

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · internal/config/config.go (reported line 106)May include surrounding context.

go
// files.write list, so the published skill never under-declares a capability.
// It asserts the negative — a real file access MISSING from the block fails
// loudly — the exact drift that previously left the `config set` write of
// config.json and the working-directory .env read unadvertised.
func TestSkillDocAdvertisesFileAccess(t *testing.T) {
	skill, err := os.ReadFile(filepath.Join(repoRoot(t), "SKILL.md"))
	if err != nil {

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · internal/config/config.go (reported line 110)May include surrounding context.

go
// files.write list, so the published skill never under-declares a capability.
// It asserts the negative — a real file access MISSING from the block fails
// loudly — the exact drift that previously left the `config set` write of
// config.json and the working-directory .env read unadvertised.
func TestSkillDocAdvertisesFileAccess(t *testing.T) {
	skill, err := os.ReadFile(filepath.Join(repoRoot(t), "SKILL.md"))
	if err != nil {

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · internal/config/config.go (reported line 113)May include surrounding context.

go
// files.write list, so the published skill never under-declares a capability.
// It asserts the negative — a real file access MISSING from the block fails
// loudly — the exact drift that previously left the `config set` write of
// config.json and the working-directory .env read unadvertised.
func TestSkillDocAdvertisesFileAccess(t *testing.T) {
	skill, err := os.ReadFile(filepath.Join(repoRoot(t), "SKILL.md"))
	if err != nil {

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · internal/config/config.go (reported line 139)May include surrounding context.

go
// files.write list, so the published skill never under-declares a capability.
// It asserts the negative — a real file access MISSING from the block fails
// loudly — the exact drift that previously left the `config set` write of
// config.json and the working-directory .env read unadvertised.
func TestSkillDocAdvertisesFileAccess(t *testing.T) {
	skill, err := os.ReadFile(filepath.Join(repoRoot(t), "SKILL.md"))
	if err != nil {

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · internal/config/config.go (reported line 140)May include surrounding context.

go
// files.write list, so the published skill never under-declares a capability.
// It asserts the negative — a real file access MISSING from the block fails
// loudly — the exact drift that previously left the `config set` write of
// config.json and the working-directory .env read unadvertised.
func TestSkillDocAdvertisesFileAccess(t *testing.T) {
	skill, err := os.ReadFile(filepath.Join(repoRoot(t), "SKILL.md"))
	if err != nil {

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · internal/config/config.go (reported line 265)May include surrounding context.

go
// files.write list, so the published skill never under-declares a capability.
// It asserts the negative — a real file access MISSING from the block fails
// loudly — the exact drift that previously left the `config set` write of
// config.json and the working-directory .env read unadvertised.
func TestSkillDocAdvertisesFileAccess(t *testing.T) {
	skill, err := os.ReadFile(filepath.Join(repoRoot(t), "SKILL.md"))
	if err != nil {

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · internal/config/config.go (reported line 267)May include surrounding context.

go
// files.write list, so the published skill never under-declares a capability.
// It asserts the negative — a real file access MISSING from the block fails
// loudly — the exact drift that previously left the `config set` write of
// config.json and the working-directory .env read unadvertised.
func TestSkillDocAdvertisesFileAccess(t *testing.T) {
	skill, err := os.ReadFile(filepath.Join(repoRoot(t), "SKILL.md"))
	if err != nil {

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · internal/config/config.go (reported line 290)May include surrounding context.

go
// files.write list, so the published skill never under-declares a capability.
// It asserts the negative — a real file access MISSING from the block fails
// loudly — the exact drift that previously left the `config set` write of
// config.json and the working-directory .env read unadvertised.
func TestSkillDocAdvertisesFileAccess(t *testing.T) {
	skill, err := os.ReadFile(filepath.Join(repoRoot(t), "SKILL.md"))
	if err != nil {

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · internal/config/config_test.go (reported line 175)May include surrounding context.

go
// files.write list, so the published skill never under-declares a capability.
// It asserts the negative — a real file access MISSING from the block fails
// loudly — the exact drift that previously left the `config set` write of
// config.json and the working-directory .env read unadvertised.
func TestSkillDocAdvertisesFileAccess(t *testing.T) {
	skill, err := os.ReadFile(filepath.Join(repoRoot(t), "SKILL.md"))
	if err != nil {

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · internal/config/config_test.go (reported line 273)May include surrounding context.

go
// files.write list, so the published skill never under-declares a capability.
// It asserts the negative — a real file access MISSING from the block fails
// loudly — the exact drift that previously left the `config set` write of
// config.json and the working-directory .env read unadvertised.
func TestSkillDocAdvertisesFileAccess(t *testing.T) {
	skill, err := os.ReadFile(filepath.Join(repoRoot(t), "SKILL.md"))
	if err != nil {

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · internal/config/config_test.go (reported line 285)May include surrounding context.

go
// files.write list, so the published skill never under-declares a capability.
// It asserts the negative — a real file access MISSING from the block fails
// loudly — the exact drift that previously left the `config set` write of
// config.json and the working-directory .env read unadvertised.
func TestSkillDocAdvertisesFileAccess(t *testing.T) {
	skill, err := os.ReadFile(filepath.Join(repoRoot(t), "SKILL.md"))
	if err != nil {

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · internal/config/config_test.go (reported line 288)May include surrounding context.

go
// files.write list, so the published skill never under-declares a capability.
// It asserts the negative — a real file access MISSING from the block fails
// loudly — the exact drift that previously left the `config set` write of
// config.json and the working-directory .env read unadvertised.
func TestSkillDocAdvertisesFileAccess(t *testing.T) {
	skill, err := os.ReadFile(filepath.Join(repoRoot(t), "SKILL.md"))
	if err != nil {

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · internal/config/config_test.go (reported line 312)May include surrounding context.

go
// files.write list, so the published skill never under-declares a capability.
// It asserts the negative — a real file access MISSING from the block fails
// loudly — the exact drift that previously left the `config set` write of
// config.json and the working-directory .env read unadvertised.
func TestSkillDocAdvertisesFileAccess(t *testing.T) {
	skill, err := os.ReadFile(filepath.Join(repoRoot(t), "SKILL.md"))
	if err != nil {

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · internal/config/config_test.go (reported line 326)May include surrounding context.

go
// files.write list, so the published skill never under-declares a capability.
// It asserts the negative — a real file access MISSING from the block fails
// loudly — the exact drift that previously left the `config set` write of
// config.json and the working-directory .env read unadvertised.
func TestSkillDocAdvertisesFileAccess(t *testing.T) {
	skill, err := os.ReadFile(filepath.Join(repoRoot(t), "SKILL.md"))
	if err != nil {

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · internal/config/config_test.go (reported line 345)May include surrounding context.

go
// files.write list, so the published skill never under-declares a capability.
// It asserts the negative — a real file access MISSING from the block fails
// loudly — the exact drift that previously left the `config set` write of
// config.json and the working-directory .env read unadvertised.
func TestSkillDocAdvertisesFileAccess(t *testing.T) {
	skill, err := os.ReadFile(filepath.Join(repoRoot(t), "SKILL.md"))
	if err != nil {

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · internal/config/config_test.go (reported line 350)May include surrounding context.

go
// files.write list, so the published skill never under-declares a capability.
// It asserts the negative — a real file access MISSING from the block fails
// loudly — the exact drift that previously left the `config set` write of
// config.json and the working-directory .env read unadvertised.
func TestSkillDocAdvertisesFileAccess(t *testing.T) {
	skill, err := os.ReadFile(filepath.Join(repoRoot(t), "SKILL.md"))
	if err != nil {

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · internal/config/config_test.go (reported line 353)May include surrounding context.

go
// files.write list, so the published skill never under-declares a capability.
// It asserts the negative — a real file access MISSING from the block fails
// loudly — the exact drift that previously left the `config set` write of
// config.json and the working-directory .env read unadvertised.
func TestSkillDocAdvertisesFileAccess(t *testing.T) {
	skill, err := os.ReadFile(filepath.Join(repoRoot(t), "SKILL.md"))
	if err != nil {

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · internal/config/config_test.go (reported line 358)May include surrounding context.

go
// files.write list, so the published skill never under-declares a capability.
// It asserts the negative — a real file access MISSING from the block fails
// loudly — the exact drift that previously left the `config set` write of
// config.json and the working-directory .env read unadvertised.
func TestSkillDocAdvertisesFileAccess(t *testing.T) {
	skill, err := os.ReadFile(filepath.Join(repoRoot(t), "SKILL.md"))
	if err != nil {

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · internal/config/config_test.go (reported line 360)May include surrounding context.

go
// files.write list, so the published skill never under-declares a capability.
// It asserts the negative — a real file access MISSING from the block fails
// loudly — the exact drift that previously left the `config set` write of
// config.json and the working-directory .env read unadvertised.
func TestSkillDocAdvertisesFileAccess(t *testing.T) {
	skill, err := os.ReadFile(filepath.Join(repoRoot(t), "SKILL.md"))
	if err != nil {

Static analysis

No suspicious patterns detected.