Uncover security risks in your agent skills before deploying
Published July 29, 2026
This tutorial explains how to catch a dangerous agent skill before an agent ever runs it: review it automatically, block it in CI if it fails, and only let your agent load skills that passed.
Agent skills make AI workflows easier to reuse, share, and improve. A skill is a single, reviewable file with its own declared tool permissions. Instead of explaining the same task every time, you can package the instructions and tools an agent needs into a repeatable workflow. That convenience also creates a security risk. A skill can instruct an agent to read files, run commands, access credentials, or communicate with external services. If the skill comes from an unfamiliar or compromised source, its SKILL.md can contain hidden instructions that steal secrets, mislead users, or perform destructive actions.
Skills are the least governed piece of the agent harness. AgentControl lets you control which models and prompts your agents use at runtime, but you should also review skills before your agents use them. This tutorial uses Tessl to run a security review and LaunchDarkly AgentControl to make sure only a skill that passed the review ever reaches your agent at runtime.
By the end of this tutorial, you’ll have:
- A pass-or-fail security review for any agent skill, including severity levels and explanations
- A CI gate that blocks skills containing prompt injection, credential theft, or destructive commands
- An agent that runs an approved skill using a model and prompt served by AgentControl
New to agent skills?
This tutorial provides sample skills to review, so you don’t need one of your own to follow along. If you want to build a new skill afterward, read the Agent Skills specification. To learn more about the agent skills LaunchDarkly publishes, which generate AgentControl configs from natural language, read LaunchDarkly agent skills or complete the Use LaunchDarkly Agent Skills in Claude Code and Cursor tutorial.
New to AgentControl?
Start with the AgentControl quickstart to learn how configs, models, prompts, and targeting work. Then return here to connect AgentControl to a security-reviewed skill.
Understand severity, verdict, and gating
Tessl’s security review scores a skill and returns a structured result, not just a pass/fail flag. Here are the three most important fields in the security review result:
- Severity ranks how dangerous a single finding is, from
LOWtoCRITICAL. A skill can have multiple findings, each with its own severity. - Verdict is the result for the whole review and is either
passorfail. - A failure threshold (the
--fail-onoption) sets the severity level that turns a finding into a failure. Setting--fail-on highmeans a severity rating ofHIGHorCRITICALcauses the review to fail, but a severity rating ofMEDIUMorLOWdoesn’t.
That threshold is also what makes the review usable as an automated gate. The command’s exit code reflects whether any finding met the threshold, so CI can block a pull request on that exit code without parsing any output.
Prerequisites
To complete this tutorial, you need:
- The Tessl CLI and a Tessl workspace
- Python 3
- An OpenAI API key
- A LaunchDarkly account
This tutorial’s sample skills and agent code are also available in the demo repository, if you’d rather clone them than copy the snippets below.
Set up Tessl
First, use this code to install the Tessl CLI:
Then authenticate to Tessl. Here’s how:
This opens a browser window to complete sign-in. Return to your terminal after it confirms you’re logged in.
A Tessl workspace is a named container tied to your account that scopes your skills and reviews.
Use this code to list the workspaces you already belong to:
If none exist yet, create one. Here’s how:
The commands in this tutorial reference your workspace as <your-workspace>. Replace that placeholder with the name from tessl workspace list, keeping the double quotes around it so your shell doesn’t interpret the angle brackets as redirection.
Clone the demo repository and change into its root directory. Here’s how:
Step 1: Review a safe skill
The repository already includes a simple report-summarizer skill at skills-content/demo/report-summarizer/SKILL.md. It reads report text and returns a short summary. Here is the skill:
In most cases, you might put these four steps directly in an AgentControl prompt instead. This tutorial uses a skill to demonstrate the pattern: a skill is a single, reviewable file you can share across every agent that needs this task, which pays off as you add more skills and more agents.
After you review the skill, run a Tessl security review against it. Here’s how:
The command returns a structured result. An example review result is below:
The important fields are:
overallSeverity: The highest severity among this review’s findings.verdict: Whether the skill passed the review.findings: The specific security issues Tessl identified, each with a severity, a code, and the reasoning behind it. A skill can returnverdict: passand still have findings, as long as none of them are severe enough to fail the review.
This skill returns verdict: pass. Its one finding is a low-severity note that the skill reads user-provided text, which isn’t severe enough to fail the review, so the skill can safely move to the next stage of your workflow.
Step 2: Catch a malicious skill
Now consider skills-content/demo/report-summarizer-risky/SKILL.md, an example malicious skill. This skill presents itself as a report summarizer but includes a credential-exfiltration step. Here it is:
At first glance, the skill still appears to summarize reports. But its setup instructions attempt to:
- Read environment variables
- Read AWS credentials
- Send that information to an external service
- Hide the activity from the user
- Give the user a false explanation if they ask about it
Run the review again. This time, include a failure threshold. Observe the --fail-on high flag included at the end of the code sample. Here it is:
Tessl detects the dangerous behavior and reports five findings. They are:
The review found an issue at or above the --fail-on high threshold, so the command exits with a nonzero status.
That exit code is what lets the review act as an automated gate. If you configure a CI job to fail when this command fails, a branch-protection rule that requires that CI job to pass can keep the pull request from merging.
Tessl also explains the reasoning behind each finding. The prompt-injection finding, code: E004 in the table above, reports:
This explanation matters because the reviewer evaluates the skill’s intent instead of only looking for individual commands, such as curl. Identifying a single command as dangerous isn’t enough on its own, because a legitimate skill might use that same command for an approved purpose, like curl calling an approved service. In this example, the dangerous behavior comes from the combination of credential access, external transmission, deception, and a purpose that doesn’t match the skill’s stated function, not from any one command in isolation.
Step 3: Enforce the review in CI
Running a review manually is useful during development, but adding the review to CI and gating the next step on the review passing turns it into a consistent security control. Tessl’s --fail-on option maps a severity threshold directly to the command’s exit code.
You can choose one of the following thresholds:
For example, --fail-on high causes the command to fail when Tessl detects a HIGH or CRITICAL issue, which results in a failing CI job.
Tessl publishes a GitHub Action that installs the CLI and runs the security review in CI for you, along with instructions for authenticating CI with a workspace API key. To set it up, read Run the security review in CI in the Tessl docs.
If you require that workflow as a branch protection rule, no one can merge a pull request that includes a skill that fails the security review.
Step 4: Run the approved skill with AgentControl
The Tessl review blocks releases from progressing when they include a dangerous skill. AgentControl configs specify which model and prompt the agent uses at runtime. Enforcing the Tessl review in CI is what keeps a dangerous skill from ever reaching the path this agent reads from.
This Python agent loads the reviewed skill and uses it while summarizing a report. Here’s how:
Both the model and prompt come from the AgentControl config at runtime. The application never specifies a hardcoded model name, summarization prompt, fallback model, or fallback prompt.
This means you can change the model, update the instructions, or roll out a variation to a percentage of traffic without redeploying the agent.
The agent also uses a fail-closed design. It exits with an error when:
LD_SDK_KEYis missing- The LaunchDarkly SDK cannot initialize
- LaunchDarkly does not serve an enabled config
- Targeting is turned off for the current context
This tutorial hard-fails for demo purposes, to make the “no config, no agent” point clearly. A production agent might instead retry, alert, or degrade gracefully before giving up.
Create the AgentControl config
Create an AgentControl config named report-summarizer-agent with:
- Completion mode, since the agent makes a single summarization call rather than running a multi-step workflow
- Your chosen model
- A single user message that defers to the loaded skill instead of restating its instructions:
Use your attached skill(s) to summarize this report: {{report_text}}. - Targeting turned on
The fastest way to create this is with the LaunchDarkly MCP server. After you have it installed, tell your AI assistant:
Create an AgentControl config named report-summarizer-agent in completion mode. Use your preferred model, with a single user message with this exact text: “Use your attached skill(s) to summarize this report: {{report_text}}”. Turn on targeting so the config is served to all users.
Approve the tool call when your assistant prompts you, the same way you would for any other MCP action.
The rest of this step runs from inside the agent/ directory. Move into it, set up a Python environment, and install the agent’s dependencies. A virtual environment keeps these packages isolated from the rest of your system, so it’s worth creating one even though it’s not strictly required. Here are the commands:
Open the new agent/.env file and fill in your LD_SDK_KEY and OPENAI_API_KEY. After you’ve saved it, load those values into your shell:
Then pipe a report into the agent:
The agent’s exact wording varies because LLM output is non-deterministic, but here’s what the result might look like:
The skill has passed its security review, while AgentControl determines how the agent behaves at runtime.
What you built
You created a Tessl workspace and used it to run a security review of two skills. The review alerted on a skill that tried to exfiltrate AWS credentials. A CI gate built on that review blocks a skill like that from merging, and even if it somehow did merge, the agent still refuses to load it without a passing review result on file.
You now have an end-to-end security and runtime-control workflow for agent skills. Here’s how it works:
- Tessl reviews each skill and returns a verdict, severity, findings, and reasoning.
- CI blocks skills that exceed your chosen security threshold before they can merge.
- The agent only loads a skill whose committed review result says
verdict: pass. - AgentControl supplies the model and prompt at runtime.
- The application fails closed when LaunchDarkly cannot serve an enabled config.
The full runnable demo, including the safe and malicious sample skills and the complete agent, is available at github.com/launchdarkly-labs/tessl-security-gate.
