Grades are a hygiene check, not a security guarantee.

Claude Code, Skill

Asl Harness

Asl Harness, a skill listed in the PlugVet directory.

Compatibility: Not stated.

Grade D

Two or more Medium findings, or a High. Only try it in a sandbox.

Graded by PlugVetRubric v2

Not a guarantee. A hygiene check of the source at the commit shown, not a security audit.

NETWORK_HOST_FROM_INPUT SECRETS_PATH_CREDENTIAL

Why this grade

  • NETWORK_HOST_FROM_INPUT Medium

    A file takes a URL or endpoint as input and has no fixed host list.

    desktop/main.cjs, line 378 readHandle('repositoryOverview','asl:repository-overview',([url,document],signal)=>readOverview(url,(...args)=>net.fetch(...args),signal,document));

  • SECRETS_PATH_CREDENTIAL Medium

    Mentions a credential store path (~/.ssh, id_rsa, .npmrc, .aws/credentials, credentials.json, .netrc).

    src/asl_harness/portable.py, line 32 SECRET_NAMES = {".env", "credentials.json", "credentials.yaml", ".credentials.yaml", "auth.json", "id_rsa", "id_ed25519"}

Any High, or two or more Medium findings, give D. One Medium gives C. Low findings or a security Note give B. Each code names a finding behind the letter.

If you think this grade is wrong, email [email protected] with the entry link and your evidence. We aim to acknowledge within two working days and decide within seven. If we confirm an error, we aim to correct it within 24 hours of confirming it. These are aims, not guarantees.

Grades are a hygiene check of the source at the commit shown, on the date checked, not a security audit or a guarantee. The free directory grades are automated. Before release, a PlugVet engineer reviews every paid report and any grade we hold back, including all F grades, against the source code and the evidence we quote.

What was checked

Plain notes from the hygiene checks.

  • Permissions

    Concern

    can delete files.

    PERMISSION_DELETE_FILES

    desktop/repository-import.cjs, line 48

    finally {await fs.unlink(pending).catch(()=>{});}

    desktop/e2e/archive-browser.cjs, line 10

    t.after(()=>fs.rm(workspace,{recursive:true,force:true}));

    desktop/e2e/product-entry.cjs, line 33

    t.after(()=>fs.rm(library,{recursive:true,force:true}));

    Show more

    desktop/e2e/reading-recovery.cjs, line 10

    t.after(()=>fs.rm(library,{recursive:true,force:true}));

    desktop/e2e/skill-lifecycle.cjs, line 27

    t.after(()=>fs.rm(root,{recursive:true,force:true}));

    desktop/e2e/skill-lifecycle.cjs, line 73

    const {root,library}=await sample();t.after(()=>fs.rm(root,{recursive:true,force:true}));

    desktop/e2e/skill-lifecycle.cjs, line 101

    const {root,library,source}=await sample();t.after(()=>fs.rm(root,{recursive:true,force:true}));

    desktop/e2e/skill-lifecycle.cjs, line 135

    const {root,library,source}=await sample();t.after(()=>fs.rm(root,{recursive:true,force:true}));

    scripts/build_desktop.py, line 38

    check = subprocess.run(command, input=json.dumps({"operation": "mode.save", "id": identifier, "document": text,

    scripts/build_desktop.py, line 71

    subprocess.run([npm, "run", "build"], cwd=desktop, check=True)

    scripts/build_desktop.py, line 76

    subprocess.run([sys.executable, "-m", "PyInstaller", "--noconfirm", "--name", "asl-harness",

    scripts/build_desktop.py, line 108

    packages = subprocess.run([npm, "ls", "--omit=dev", "--all", "--parseable"],

    scripts/build_desktop.py, line 129

    subprocess.run(['codesign', '--force', '--deep', '--sign', '-', '--preserve-metadata=entitlements', str(app)], check=True)

    scripts/build_desktop.py, line 130

    subprocess.run(['codesign', '--verify', '--deep', '--strict', str(app)], check=True)

    scripts/package_library.py, line 29

    check = subprocess.run([*command, 'merge-base', '--is-ancestor', revision, 'refs/remotes/origin/main'],

    scripts/package_library.py, line 33

    raw = subprocess.check_output([*command, 'archive', '--format=zip', revision], env=environment, timeout=60)

  • Install and update

    Pass

    No install-time script found, and dependencies are pinned or absent.

  • Network

    Concern

    accepts a host from input, with no fixed list in this file. can reach a host that is not written as a fixed address.

    NETWORK_HOST_FROM_INPUT NETWORK_NO_FIXED_HOST

    desktop/main.cjs, line 378

    readHandle('repositoryOverview','asl:repository-overview',([url,document],signal)=>readOverview(url,(...args)=>net.fetch(...args),signal,document));

    desktop/main.cjs, line 382

    return readRepository(url, { fetch: (...args) => net.fetch(...args),

    desktop/main.cjs, line 410

    const request = (url, settings = {}) => net.fetch(url, { ...settings, signal: AbortSignal.any([signal, settings.signal].filter(Boolean)) });

    Show more

    desktop/main.cjs, line 414

    discover(provider, query, (url, settings = {}) => net.fetch(url, { ...settings, signal: AbortSignal.any([signal, settings.signal].filter(Boolean)) })),

    desktop/market.cjs, line 38

    const response = await fetch(url, {

    desktop/repository-import.cjs, line 57

    const request=(url,options={})=>fetch(url,{...options,signal:AbortSignal.any([signal,AbortSignal.timeout(20000)].filter(Boolean))});

    desktop/repository-import.cjs, line 75

    const request = (url, options = {}) => fetch(url, { ...options, signal: AbortSignal.any([signal, options.signal].filter(Boolean)) });

    desktop/e2e/fixture.cjs, line 50

    return net.fetch(selected,{bypassCustomProtocolHandlers:true});

  • Secrets

    Concern

    reads a credential or environment file.

    SECRETS_PATH_CREDENTIAL SECRETS_PATH_DOTENV

    src/asl_harness/archives.py, line 55

    if (lower in SECRET_NAMES or lower.startswith('.env.') and lower != '.env.example'

    src/asl_harness/deepseek.py, line 46

    or name == ".env"

    src/asl_harness/portable.py, line 32

    SECRET_NAMES = {".env", "credentials.json", "credentials.yaml", ".credentials.yaml", "auth.json", "id_rsa", "id_ed25519"}

    Show more

    src/asl_harness/deepseek.py, line 47

    or (name.startswith(".env.") and name != ".env.example")

    src/asl_harness/history.py, line 119

    if any(part.lower() in SECRET_NAMES or part in OMIT or part.lower().startswith('.env') for part in path.parts):

    src/asl_harness/management.py, line 225

    or lower.startswith('.env.') and lower != '.env.example' or path.suffix.lower() in SECRET_FILE_SUFFIXES):

    src/asl_harness/portable.py, line 93

    if (lower in SECRET_NAMES or lower.startswith(".env.") and lower != ".env.example"

    src/asl_harness/workspace.py, line 26

    SECRET_FILE_NAMES = {".env", "id_ed25519", "id_rsa"}

  • Agent instructions

    Pass

    No instruction to ignore earlier instructions or to hide a step.

  • Obfuscation

    Concern

    contains a minified payload.

    OBFUSCATION_MINIFIED_PAYLOAD

    desktop/e2e/direct-canvas.cjs, line 30

    const during=await center(el);assert.ok(Math.abs(during.x-before.x-dx)<1&&Math.abs(during.y-before.y-dy)<1,'拖动实时跟手,包含缩放换算 '+JSON.stringify({before,during,dx,dy,hit:await el.evaluat

    desktop/e2e/panels.cjs, line 42

    await page.evaluate(()=>{window.__panelWheel=[];document.addEventListener('wheel',event=>{const tree=document.querySelector('.package-tree'),box=tree.getBoundingClientRect();window

    desktop/e2e/panels.cjs, line 84

    }catch(error){console.error(run,error.stack);console.error('目录滚动事实:',await page.evaluate(()=>({events:window.__panelWheel,trees:[...document.querySelectorAll('.package-tree')].map(

  • Licence

    Pass

    A licence file is present (LICENSE).

  • Maintainer activity

    Pass

    The latest commit is within the last 365 days.

Note

Hygiene check of commit 21e386fadcd8. can delete files. accepts a host from input, with no fixed list in this file. can reach a host that is not written as a fixed address. reads a credential or environment file. contains a minified payload.

Date and source

Date checked:

View source

github.com

Commit graded: 21e386fadcd8b83c6a5044904adb55afb34522fb

This link goes to the original host. PlugVet does not host or check what you download from there, and installing it is your own decision and risk.

Read how this letter is chosen