SkillTotal

Is @anthropic-ai/claude-agent-sdk safe?

No malicious indicators - review capabilities before installing
Notable — review in context (capabilities are not malware):
  • Node.js shell/command execution
  • Node.js dynamic code execution
  • Node.js filesystem read

@anthropic-ai/claude-agent-sdk is an AI npm_package analyzed by SkillTotal's deterministic static scanner. The scan found no malicious indicators, though 7 risky constructs are reported for review. It can: delegated authentication, dynamic code execution, filesystem read, filesystem write, mcp tools detected, network egress and shell execution — capabilities are what the code can do, not a verdict on intent. Risk score 0/100 (low).

@anthropic-ai/claude-agent-sdk 0.3.233

npm_package · npm:@anthropic-ai/claude-agent-sdk
LOW
0
/ 100 risk score
Snapshot · scanned Aug 15, 2026 · @anthropic-ai/claude-agent-sdk@0.3.233 · engine 0.38.1 / ruleset 42

Automated static-analysis result. It can contain false positives and false negatives, and is not a claim about the intent of @anthropic-ai/claude-agent-sdk's authors. Report a false positive.

Capabilities — what this component can do (not a risk score):
delegated authenticationdynamic code executionfilesystem readfilesystem writemcp tools detectednetwork egressshell execution

Behavioral traits

How this component maps to the CSA agentic threat model. Descriptive — it never affects the risk score.

Tool surface
Tool Usage
Execution authority
Tool Access Control / Direct Tool Access
Filesystem reach
Tool Execution Context
Network egress
Interaction & Communication / Direct Communication
Delegated authentication
Tool Execution Context / User Delegated Credentials

Findings (7)

HIGHNode.js dynamic code executionST-DYN-NODE

The code turns strings into live code at runtime (eval / new Function / exec).

`+o}}catch(s){}}throw r}}_request(e,t){if(typeof e==="string")t=t||{},t.url=e;else t=e||{};t=$n(this.defaults,t);let{transitional:r,paramsSerializer:n,headers:o}=t;if(r!==void 0)RA.assertOptions(r,{silentJSONParsing:_n.transitional(_n.boole …
deps: ${r}}`};var HY={keyword:"dependencies",type:"object",schemaType:"object",error:W8.error,code(e){let[t,r]=ZY(e);H8(e,t),Z8(e,r)}};function ZY({schema:e}){let t={},r={};for(let i in e){if(i==="__proto__")continue;let n=Array.isArray(e[i …
`," ").trim();if(!i)continue;let a=dY.exec(i);if(a){if(!t.commandFallback)t.commandFallback=a[1];continue}let c=/<bash-input>([\s\S]*?)<\/bash-input>/.exec(i);if(c)return`! ${c[1].trim()}`;if(uY.test(i))continue;if(i.length>200)i=em(i,200). …

Why it matters: If those strings aren't fixed and trusted, they become a way to run arbitrary code.

Fix: Avoid evaluating dynamically constructed code; if unavoidable, ensure the input is a trusted constant and never derived from external data.

HIGHNode.js shell/command executionST-SHELL-NODE

The component can run operating-system commands or spawn processes.

import{createRequire as $6}from"node:module";var F6=Object.create;var{getPrototypeOf:M6,defineProperty:yy,getOwnPropertyNames:U6}=Object;var P6=Object.prototype.hasOwnProperty;function H6(e){return this[e]}var G6,W6,gc=(e,t,r)=>{var n=e!=nu …
*/var ah=XR(),PX=v("path").extname,ek=/^\s*([^;\s]*)(?:;|\s|$)/,HX=/^text\//i;$X.charset=tk;$X.charsets={lookup:tk};$X.contentType=GX;$X.extension=WX;$X.extensions=Object.create(null);$X.lookup=YX;$X.types=Object.create(null);qX($X.extensio …
`).forEach(function(i){if(o=i.indexOf(":"),r=i.substring(0,o).trim().toLowerCase(),n=i.substring(o+1).trim(),!r||t[r]&&qee[r])return;if(r==="set-cookie")if(t[r])t[r].push(n);else t[r]=[n];else t[r]=t[r]?t[r]+", "+n:n}),t};var ED=m(()=>{ze() …
`)}getSetCookie(){return this.get("set-cookie")||[]}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(e){return e instanceof this?e:new this(e)}static concat(e,...t){let r=new this(e);return t.forEach((n)=>r.set(n)),r}static access …
`).map((t)=>t.trim()).join(" ")};LD.O=function(e){return this.inspectOpts.colors=this.useColors,wh.inspect(e,this.inspectOpts)}});var Qh=B(function(AJe,aI){if(typeof process>"u"||process.type==="renderer"||!1||process.__nwjs)aI.exports=vD() …
`+o}}catch(s){}}throw r}}_request(e,t){if(typeof e==="string")t=t||{},t.url=e;else t=e||{};t=$n(this.defaults,t);let{transitional:r,paramsSerializer:n,headers:o}=t;if(r!==void 0)RA.assertOptions(r,{silentJSONParsing:_n.transitional(_n.boole …
})));`),d.write(`newResult[${la(I)}] = ${b}.value`)}d.write("payload.value = newResult;"),d.write("return payload;");let y=d.compile();return(I,b)=>y(p,I,b)},o,s=YA,i=!_g.jitless,c=i&&fb.value,u=t.catchall,A;e._zod.parse=(p,d)=>{A??(A=r.val …
`);r.enqueue(s)}catch(n){r.error(n)}},async cancel(){await t.return?.()}})}}});async function Xg(e,t){let{response:r,requestLogID:n,retryOfRequestLogID:o,startTime:s}=t,i=await(async()=>{if(t.options.stream)return Se(e).debug("response",r.s …
`)){let o=n.trim().split("/").filter((i)=>i!==""&&i!==".");if(o.length===0)continue;let s=o[0];if(t===void 0)t=s;else if(s!==t)return"";if(o.length>1)r=!0}return t!==void 0&&r?t:""}async function tB(e,t){let r=lr.join(t,`.skill-archive-${pr …
${p}`;return{output:p,exitCode:A}}close(){if(E(this,xa,"f"))return;D(this,xa,!0,"f");let e=E(this,Rn,"f");D(this,Rn,null,"f"),e?.resolve(),E(this,vr,"f").stdout.destroy(),E(this,vr,"f").stderr.destroy(),E(this,vr,"f").stdin.destroy();try{pr …
`)}})}function VH(e){return ba({name:"grep",description:"Search file contents for a regex. Uses ripgrep if available, otherwise a built-in walker.",inputSchema:{type:"object",properties:{pattern:{type:"string"},path:{type:"string"}},require …
`;if(this.toStderr){this.deps.writeToStderr(n);return}this.write(n)}write(e){let t=this.getWriter(),r=ode;if(this.storageV5!==void 0){if(r={sessionId:this.deps.sessionId(),fromBackend:sb.getStore()===!0},r.fromBackend)this.backendLinesLogge …
`)};var WY=m(()=>{GY=sfe("execa").enabled});import{Buffer as cfe}from"node:buffer";import lfe from"node:path";import YS from"node:child_process";import aE from"node:process";function cE(e,t,r){let n=$Y(e,t,r),o=PS(e,t),s=HS(e,t);WS(s,n.opti …
`;break;case"t":r+="\t";break;case"b":r+="\b";break;case'"':r+='"';break;case"\\":r+="\\";break;default:r+=i;break}o+=2;continue}if(i==="\\"){r+="\\",o+=2;continue}}r+=s,o++}if(!n)r=P0e(r);return r}function P0e(e){let t=e.length;while(t>0&& …
`)||e.includes("\r")||e.includes("\x00"))===!1}function qNe(e,t){let{headersList:r}=t,n=(r.get("referrer-policy",!0)??"").split(","),o="";if(n.length>0)for(let s=n.length;s!==0;s--){let i=n[s-1].trim();if(QNe.has(i)){o=i;break}}if(o!=="")e. …
https://github.com/browserify/crypto-browserify`)},e.constants={DH_CHECK_P_NOT_SAFE_PRIME:2,DH_CHECK_P_NOT_PRIME:1,DH_UNABLE_TO_CHECK_GENERATOR:4,DH_NOT_SUITABLE_GENERATOR:8,NPN_ENABLED:1,ALPN_ENABLED:1,RSA_PKCS1_PADDING:1,RSA_SSLV23_PADDIN …
|| ${s} === "boolean" || ${n} === null`).assign(f,gt._`[${n}]`)}}}function iV({gen:e,parentData:t,parentDataProperty:r},i){e.if(gt._`${t} !== undefined`,()=>e.assign(gt._`${t}[${r}]`,i))}function mS(e,t,r,i=hu.Correct){let n=i===hu.Correct? …
deps: ${r}}`};var HY={keyword:"dependencies",type:"object",schemaType:"object",error:W8.error,code(e){let[t,r]=ZY(e);H8(e,t),Z8(e,r)}};function ZY({schema:e}){let t={},r={};for(let i in e){if(i==="__proto__")continue;let n=Array.isArray(e[i …
Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.`);else if(r.cycles==="ref")o(y);continue}if(_.count>1){if(r.reused==="ref"){o(y);continue}}}let s=(y,_)=>{let p=this.seen.get(y),w=p.def??p.schema,x={...w};if( …
`),raw:this.chunks};return this.event=null,this.data=[],this.chunks=[],n}if(this.chunks.push(e),e.startsWith(":"))return null;let[t,r,i]=fF(e,":");if(i.startsWith(" "))i=i.substring(1);if(t==="event")this.event=i;else if(t==="data")this.dat …
*   // stdin-EOF + ~2 s grace window, so passing it to spawn()/your
* it (Node `spawn({signal})` → `child.kill()`, VM/container teardown,
* Why: passing the caller's raw signal to Node `spawn()` registers
import{createRequire as K5}from"node:module";var M5=Object.create;var{getPrototypeOf:N5,defineProperty:rb,getOwnPropertyNames:U5}=Object;var z5=Object.prototype.hasOwnProperty;function F5(e){return this[e]}var j5,$5,Ea=(e,t,n)=>{var r=e!=nu …
`);n.enqueue(s)}catch(r){n.error(r)}},async cancel(){await t.return?.()}})}}});async function Fm(e,t){let{response:n,requestLogID:r,retryOfRequestLogID:o,startTime:s}=t,i=await(async()=>{if(t.options.stream)return Ue(e).debug("response",n.s …

Why it matters: Powerful and often legitimate — confirm the commands aren't built from untrusted input.

Fix: Confirm the command and its arguments are fully controlled and not derived from untrusted input; prefer execFile with an argument array.

MEDIUMNode.js filesystem readST-FS-NODE-READ

The component reads files from disk.

`)){let A=u.indexOf(":");if(A>=0)c[u.substring(0,A).trim()]=u.substring(A+1).trim()}s.defaultHeaders={...c,...s.defaultHeaders}}let a=o.__auth;if(delete s.__auth,delete s.__baseURLIsExplicit,this._options=s,this.apiKey=typeof t==="string"?t …
`)){let _=y.indexOf(":");if(_>=0)g[y.substring(0,_).trim()]=y.substring(_+1).trim()}o.defaultHeaders={...g,...o.defaultHeaders}}let f=n.__auth;if(delete o.__auth,delete o.__baseURLIsExplicit,this._options=o,this.apiKey=typeof t==="string"?t …
`;if(this.toStderr){this.deps.writeToStderr(r);return}this.write(r)}write(e){let t=this.getWriter(),n=Hae;if(this.storageV5!==void 0){if(n={sessionId:this.deps.sessionId(),fromBackend:Hw.getStore()===!0},n.fromBackend)this.backendLinesLogge …

Why it matters: Usually legitimate, but worth confirming it can't be steered into reading sensitive files.

Fix: Confirm which files are read and that paths cannot be influenced by untrusted input to reach sensitive locations.

MEDIUMNode.js filesystem write/deleteST-FS-NODE-WRITE

The component writes or deletes files on disk.

`)){let A=u.indexOf(":");if(A>=0)c[u.substring(0,A).trim()]=u.substring(A+1).trim()}s.defaultHeaders={...c,...s.defaultHeaders}}let a=o.__auth;if(delete s.__auth,delete s.__baseURLIsExplicit,this._options=s,this.apiKey=typeof t==="string"?t …
`)){let _=y.indexOf(":");if(_>=0)g[y.substring(0,_).trim()]=y.substring(_+1).trim()}o.defaultHeaders={...g,...o.defaultHeaders}}let f=n.__auth;if(delete o.__auth,delete o.__baseURLIsExplicit,this._options=o,this.apiKey=typeof t==="string"?t …
`;if(this.toStderr){this.deps.writeToStderr(r);return}this.write(r)}write(e){let t=this.getWriter(),n=Hae;if(this.storageV5!==void 0){if(n={sessionId:this.deps.sessionId(),fromBackend:Hw.getStore()===!0},n.fromBackend)this.backendLinesLogge …

Why it matters: Usually legitimate, but worth confirming the paths can't be controlled by untrusted input.

Fix: Confirm which files are written/deleted and that paths cannot be influenced by untrusted input.

MEDIUMNode.js network egressST-NET-NODE

The component makes outbound network requests.

import{createRequire as $6}from"node:module";var F6=Object.create;var{getPrototypeOf:M6,defineProperty:yy,getOwnPropertyNames:U6}=Object;var P6=Object.prototype.hasOwnProperty;function H6(e){return this[e]}var G6,W6,gc=(e,t,r)=>{var n=e!=nu …
`,ire,are=2,cre=(e,t,r)=>{let{tag:n="form-data-boundary",size:o=25,boundary:s=n+"-"+Qe.generateString(o,sre)}=r||{};if(!_.isFormData(e))throw TypeError("FormData instance required");if(s.length<1||s.length>70)throw Error("boundary must be 1 …
})));`),d.write(`newResult[${la(I)}] = ${b}.value`)}d.write("payload.value = newResult;"),d.write("return payload;");let y=d.compile();return(I,b)=>y(p,I,b)},o,s=YA,i=!_g.jitless,c=i&&fb.value,u=t.catchall,A;e._zod.parse=(p,d)=>{A??(A=r.val …
`;break;case"t":r+="\t";break;case"b":r+="\b";break;case'"':r+='"';break;case"\\":r+="\\";break;default:r+=i;break}o+=2;continue}if(i==="\\"){r+="\\",o+=2;continue}}r+=s,o++}if(!n)r=P0e(r);return r}function P0e(e){let t=e.length;while(t>0&& …
`+i:i;break}}return t.data||r?t:null}class BR{decoder=new TextDecoder;pending=[];pendingLength=0;push(e){let t=typeof e==="string"?e:this.decoder.decode(e,pVe);if(!t)return[];return this.drain(t)}flush(){let e=this.decoder.decode();if(e)thi …
`)}else w(`SSETransport: Ignoring client_event with no type in payload: event_id=${r.event_id}`);this.onEventCallback?.(r)}handleEphemeralFrame(e){let t;try{t=ro(e)}catch(n){w(`SSETransport: Failed to parse ephemeral_event data: ${de(n)}`,{ …
Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.`);else if(r.cycles==="ref")o(y);continue}if(_.count>1){if(r.reused==="ref"){o(y);continue}}}let s=(y,_)=>{let p=this.seen.get(y),w=p.def??p.schema,x={...w};if( …
`+s:s;break}}return t.data||r?t:null}class yw{decoder=new TextDecoder;pending=[];pendingLength=0;push(e){let t=typeof e==="string"?e:this.decoder.decode(e,Wq);if(!t)return[];return this.drain(t)}flush(){let e=this.decoder.decode();if(e)this …
import{createRequire as K5}from"node:module";var M5=Object.create;var{getPrototypeOf:N5,defineProperty:rb,getOwnPropertyNames:U5}=Object;var z5=Object.prototype.hasOwnProperty;function F5(e){return this[e]}var j5,$5,Ea=(e,t,n)=>{var r=e!=nu …
`)){let u=l.indexOf(":");if(u>=0)c[l.substring(0,u).trim()]=l.substring(u+1).trim()}s.defaultHeaders={...c,...s.defaultHeaders}}let a=o.__auth;if(delete s.__auth,delete s.__baseURLIsExplicit,this._options=s,this.apiKey=typeof t==="string"?t …
`,jpe,$pe=2,Hpe=(e,t,n)=>{let{tag:r="form-data-boundary",size:o=25,boundary:s=r+"-"+He.generateString(o,Fpe)}=n||{};if(!w.isFormData(e))throw TypeError("FormData instance required");if(s.length<1||s.length>70)throw Error("boundary must be 1 …
`),c=a.pop()??"";if(c)this.partialChunks.push(c);for(let l of a){if(!l)continue;let u;try{u=mt(l)}catch(p){rn(`DirectConnect: dropped malformed JSON line (${l.length} bytes): ${p}`);continue}this.messages.enqueue(u)}}),n.addEventListener("e …
`+"\t".repeat(t))}};var o3;(function(e){e.DEFAULT={allowTrailingComma:!1}})(o3||(o3={}));var s3;(function(e){e[e.None=0]="None",e[e.UnexpectedEndOfComment=1]="UnexpectedEndOfComment",e[e.UnexpectedEndOfString=2]="UnexpectedEndOfString",e[e. …

Why it matters: Usually legitimate, but confirm the destinations are expected and no sensitive data leaves.

Fix: Confirm the destination hosts are expected and that no sensitive data is sent off-host.

LOWDelegated authentication (OAuth 2.0 / OIDC)ST-AUTH-DELEGATED

An OAuth 2.0 / OpenID Connect delegated-authentication flow was detected (authorization-code / refresh-token / token-exchange grant, an OIDC authorize/discovery endpoint or id_token, or a delegation library). Tools authenticate with the end user's delegated, scoped credentials rather than a long-lived embedded service credential.

Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.`);else if(n.cycles==="ref")s(l);continue}if(u.count>1){if(n.reused==="ref"){s(l);continue}}}let i=(l,u)=>{let p=this.seen.get(l),f=p.def??p.schema,m={...f};if( …

Fix: Delegated auth is a lower-blast-radius execution context than an embedded static credential. Confirm the requested scopes are minimal and that tokens are never logged or forwarded off-host.

LOWMCP tool surface detectedST-MCP-DETECTED

An MCP tool surface (manifest or tool definitions) was found.

]`;continue}if(n+=i[g],i[g]==="\\")o=!0;else if(s&&i[g]==="]")s=!1;else if(!s&&i[g]==="[")s=!0}try{new RegExp(n)}catch{return console.warn(`Could not convert regex pattern at ${t.currentPath.join("/")} to a flag-independent form! Falling ba …
]`;continue}if(o+=r[c],r[c]==="\\")s=!0;else if(i&&r[c]==="]")i=!1;else if(!i&&r[c]==="[")i=!0}try{new RegExp(o)}catch{return console.warn(`Could not convert regex pattern at ${t.currentPath.join("/")} to a flag-independent form! Falling ba …

Why it matters: Just context — review which tools it offers and their permissions.

Fix: Review the declared MCP tools and their permissions.

Check your own component

Run the same evidence-backed scan on any MCP server, agent skill, or package.

Scan your own component

How we determine this: deterministic static analysis (regex + AST), evidence-anchored, no code execution. Methodology →