From f1f95c88a68758662d1cc6d18bbde3f0a6b01db1 Mon Sep 17 00:00:00 2001 From: Rayan-and-beyond <263488867+Rayan-and-beyond@users.noreply.github.com> Date: Tue, 15 Sep 2026 19:42:56 +0000 Subject: [PATCH] feat: flag setuid and sudoers escalation --- CHANGELOG.md | 1 + package.json | 2 +- src/rules.js | 5 +++++ test/sh-011-privilege-persistence.test.js | 24 +++++++++++++++++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 test/sh-011-privilege-persistence.test.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 70144b9..ff297bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ All notable changes to this project are documented here, following ### Added +- **SKILL-SH-011**: flag setuid changes, root-owned payloads, and writes to `/etc/sudoers`. - **SKILL-SUP-003**: flag code that fetches scripts or packages over plaintext `http://`. - Rule `SKILL-SH-010` (critical, code): flags SSH key planting via `authorized_keys` or shell redirects into `~/.ssh/`. diff --git a/package.json b/package.json index 117b5ea..3b2a83c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@royalpinto007/skill-audit", - "version": "0.1.7", + "version": "0.1.8", "description": "Security scanner for agent skills. Scan a Claude/agent Skill for prompt-injection, dangerous shell, secret access, and exfiltration before you trust it. Zero dependencies, SARIF output, npx skill-audit .", "type": "module", "bin": { diff --git a/src/rules.js b/src/rules.js index 88b54ed..aaa1364 100644 --- a/src/rules.js +++ b/src/rules.js @@ -202,6 +202,11 @@ export const RULES = [ remediation: "Writing to authorized_keys or under ~/.ssh grants persistent remote login. Never ship this in a skill.", pattern: /(authorized_keys\b|(>>|>)\s*~?\/?\.ssh\/)/gi }, + { id: "SKILL-SH-011", severity: "high", category: "dangerous-shell", appliesTo: "code", + title: "Creates privileged executables or modifies sudoers", + remediation: "Setuid binaries, root-owned payloads, and sudoers writes can create persistent privilege escalation.", + pattern: /(chmod\s+(?:u\+s|4[0-7]{3})\s+|chown\s+root:root\s+|(?:>>|>)\s*\/etc\/sudoers\b)/gi }, + // ---- Dynamic code execution ---- { id: "SKILL-OBF-003", severity: "medium", category: "obfuscation", appliesTo: "code", title: "Dynamic code execution (exec/compile)", diff --git a/test/sh-011-privilege-persistence.test.js b/test/sh-011-privilege-persistence.test.js new file mode 100644 index 0000000..fd3e333 --- /dev/null +++ b/test/sh-011-privilege-persistence.test.js @@ -0,0 +1,24 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { scanText } from "../src/scan.js"; + +const RULE = "SKILL-SH-011"; + +test("SKILL-SH-011 flags setuid and sudoers privilege escalation", () => { + for (const source of [ + "chmod u+s /tmp/backdoor\n", + "chmod 4755 /tmp/x\n", + "chown root:root /tmp/x\n", + "echo 'user ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers\n", + ]) { + const findings = scanText(source, "setup.sh", null).filter((f) => f.rule === RULE); + assert.equal(findings.length, 1, source); + assert.equal(findings[0].severity, "high"); + } +}); + +test("SKILL-SH-011 keeps ordinary permission changes clean", () => { + for (const source of ["chmod 755 ./script.sh\n", "chown app:app ./data\n"]) { + assert.equal(scanText(source, "setup.sh", null).filter((f) => f.rule === RULE).length, 0, source); + } +});