Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
153319 stories
·
33 followers

Improving token efficiency in GitHub Agentic Workflows

1 Share

GitHub Agentic Workflows is like a team of street sweepers that clean up little messes in your repo. These teams significantly improve repo hygiene and quality, but as with all agentic work, cost is a growing concern for developers. And because CI jobs like agentic workflows are automatically scheduled and triggered, costs can accumulate out of view.

Thankfully, making automations more efficient is easier than doing the same for interactive desktop sessions. Work done during a developer session can be hard to predict, but agentic workflows’ work is fully specified in YAML and repeats every execution.

Because we maintain and use GitHub Agentic Workflows in our own GitHub repositories, we worry about token efficiency as much as our users. That is why in April 2026, we began to systematically optimize the token usage of many of the workflows that we rely on every day. This post describes what we instrumented, the optimizations we applied, and our preliminary results.

Logging token usage

We rely on hundreds of agentic workflows in our repos for maintenance and CI. All workflows run as GitHub Actions against real API rate limits. We are building the plane as we fly it and burning jet fuel as we go.

Before we could optimize our token consumption, we needed to know how tokens were consumed. The first challenge we faced was that each agent framework (Claude CLI, Copilot CLI, Codex CLI) emitted logs in a different format, and usage data could be incomplete for historical runs. Thankfully, the agentic-workflows security architecture uses an API proxy to prevent agents from directly accessing authentication credentials. This proxy gave us a way to capture token usage across all runs in a single normalized format, regardless of agent framework.

Every workflow now outputs a token-usage.jsonl artifact with one record per API call that contains input tokens, output tokens, cache-read tokens, cache-write tokens, model, provider, and timestamps. Combining this data with the rest of the workflow’s logs gave a historical view of how tokens were typically spent and allowed us to optimize for future runs.

Workflows optimizing workflows

With token data in hand, we built two daily optimization workflows.

A Daily Token Usage Auditor reads token usage artifacts from recent workflow runs, aggregates consumption by workflow, and posts a structured report. Its job is to flag any workflow that has significantly increased its recent usage, surface the most expensive workflows, and take note of anomalous runs (e.g., a workflow that normally completes in four LLM turns taking 18).

When an Auditor flags a workflow, a Daily Token Optimizer looks at the workflow’s source and recent logs to create a GitHub issue with describing concrete inefficiencies and proposing specific optimization. The Optimizer has found many inefficiencies that we would have otherwise missed.

Of course, the Auditor and Optimizer are agentic workflows themselves, and their token usage also appear in daily reports to create a small virtuous cycle.

Eliminating unused MCP tools

Based on our initial Auditor and Optimizer results, the most common inefficiency is unused MCP tool registrations.

Because LLM APIs are stateless, agent runtimes typically include the MCP tool function names and JSON schemas with each request. In practice, this means the full set of tools can become part of every call’s context. For a GitHub MCP server with 40 tools, this can add 10–15 KB of schema per turn. If the agent only uses two tools, the remaining 38 are pure overhead added to every request.

Workflow authors naturally start with a full tool-set since it is the path of least resistance, and the agent can figure out which tools it needs. But as time goes on, most workflows rely on a narrow, stable set of tools. The Optimizer identifies this pattern by cross-referencing tool manifests against actual tool calls and recommends pruning unused tools from the configuration.

In our smoke-test workflows, removing unused tools from the MCP configuration reduced per-call context size by 8–12 KB, saving several thousand tokens per run with no change in behavior.

Replacing GitHub MCP with GitHub CLI

Removing unused MCP tools is a relatively simple win. A larger structural opportunity was replacing GitHub MCP calls for data-fetching operations like retrieving pull request diffs, file contents, and review comments with calls to the GitHub CLI.

This change did more than reduce the overhead of unused tools because an MCP tool call is a reasoning step in addition to data retrieval. The agent must decide to call the tool, formulate its arguments, and receive its output as part of the context. That’s a full round-trip LLM API call, consuming tokens for the tool-use JSON schema, the argument block, and the response. Calling ‘gh pr diff’, by contrast, is a deterministic HTTP request to GitHub’s REST API with no LLM involvement.

We used two strategies for this migration:

Pre-agentic data downloads. For data that an agent will always need like a pull request diff or the list of changed files, we added setup steps in the workflow that run gh commands before the agent starts and writes the results to workspace files. The agent reads those files instead of making MCP calls. This eliminates tool-call overhead and allows the agent to take advantage of its extensive training in bash scripting to efficiently process the data.

In-agent CLI proxy substitution. Pre-downloading isn’t possible in cases where the agent determines what to fetch at runtime. In these cases we rely on a lightweight transparent HTTP proxy that routes CLI traffic to GitHub’s API servers without exposing an authentication token to the agent. The agent runs gh pr view –json and gets structured data back, just as a user would from a terminal. This reduces token usage without compromising our zero-secrets security requirement for the agent.

Together, these techniques move the majority of GitHub data-fetching out of the LLM reasoning loop.

Measuring efficiency gains is not easy

Once we began to optimize our workflows, we ran into a more nuanced problem: how do you know whether a change made things more efficient, or just made the workflow do less (and perhaps worse) work?

There are three confounding factors.

Not all tokens are created equal. Running the same workflow on Claude Haiku versus Claude Sonnet produces similar token counts but cost very differently. Haiku costs roughly 4× less per token than Sonnet, so a workflow that switches models appears unchanged in raw token count but represents a significant cost reduction. To account for this, we use an Effective Tokens (ET) metric that applies model multipliers to each token type:

ET = m × (1.0 × I + 0.1 × C + 4.0 × O) 

where m is a model cost multiplier (Haiku = 0.25×, Sonnet = 1.0×, Opus = 5.0×), I is newly-processed input tokens, C is cache-read tokens, and O is output tokens. Output tokens carry 4× weight because they are the most expensive token type across all major providers. Cache-read tokens carry only 0.1× weight because they are served from cache at a fraction of the cost of fresh input. This formula normalizes consumption across model tiers so that a 10% ET reduction means a genuine 10% cost reduction regardless of which model is in use.

The workload is a live repository. As far as we know, there is no agentic-workflow benchmark that we can use to optimize our token usage. When we began looking at token usage by our workflows, we found that in one run a workflow would handle a five-line fix, and in the next run it would handle a 200-line pull request. The first run naturally uses fewer tokens, but the difference is not due to a sudden change in efficiency. Raw token counts can confuse workload variation with fluctuations in efficiency. We try to normalize this by tracking LLM API call counts alongside token counts; constant LLM turns-per-run and falling tokens-per-call indicate genuine efficiency improvement. Both falling together may indicate that less work is being done.

Does quality change? Understanding output quality is the hardest consideration. A lighter model running a more constrained workflow might produce lower-quality output. We looked at the process-level signals like output tokens per LLM call, turn counts per run, and tool-call completion rates to approximate quality. For our optimized Smoke Copilot workflow, all three remained stable across the optimization period even as token consumption fell. The workflow completes in roughly five LLM turns every run, before and after the optimizations. Of course, these are process signals, not outcome signals. We cannot directly observe whether the quality improved, degraded, or was stable, because there is no ground-truth “correctness.” Measuring tokens-per-unit-of-correct-work requires additional instrumentation and thought.

Initial results

After deploying the auditor and optimizer across a dozen production workflows in the gh-aw and gh-aw-firewall repos, we downloaded token-usage artifacts for runs before and after each was optimized and computed ET for each run. Nine of the 12 workflows received optimizer-recommended changes. We include results only for workflows with at least eight runs in both the pre- and post-optimization periods. These are: Auto-Triage Issues, Daily Compiler Quality, Community Attribution, Security Guard, and Smoke Claude.

Graph showing token savings across Auto-Triage Issues, Daily Compiler Quality, Community Attribution, Security Guard, adn Smoke Claude.

Auto-Triage Issues shows a clear, sustained reduction of 62% across 109 post-fix runs. Daily Compiler Quality shows 19% improvement over 12 post-fix runs, and Daily Community Attribution shows 37% improvement over eight post-fix runs. In the gh-aw-firewall repo, Security Guard, which audits every pull request for security-sensitive changes, and Smoke Claude an integration test that exercises the firewall’s Claude CLI path, had the most post-fix runs and show improvements of 43% and 59%, respectively.

Run frequency matters as much as per-run savings. Auto-Triage Issues fires on every new issue (averaging 6.8 runs per day with a max of 15) while Daily Compiler Quality runs at most once per day. 62% savings and 6.8 runs/day compounds quickly: over the observation period, Auto-Triage’s optimization saved roughly 7.8 M ET in aggregate, assuming the pre-optimization rate. Security Guard and Smoke Claude run even more frequently. When prioritizing which workflows to optimize, run frequency is as important as per-run consumption.

It is important to note that not every optimization that the agent recommends translates into measurable ET savings, especially over short observation windows on a live repository where workload varies day to day. For example, the Contribution Check workflow experienced a 5% increase in ET, and we will discuss it in greater detail below.

Take aways

Based on these results, we highlight three patterns.

Many agent turns are deterministic data-gathering. Auto-Triage Issues shows the strongest sustained improvement in gh-aw (−44% across 62 post-fix runs) because the optimization eliminated structural inefficiency: many agent turns were spent on reads that required no inference, such as fetching issue metadata and scanning labels. Moving those reads into pre-agentic CLI steps before the agent starts removed them from the LLM reasoning loop entirely. The same pattern drove Security Guard’s −60% reduction in gh-aw-firewall: a relevance gate now skips the LLM entirely for pull requests that don’t touch security-sensitive files. The cheapest LLM call is the one you don’t make.

Contribution Check illustrates a confounding factor: 82–83% of input tokens were cache reads (data-gathering), but average ET increased 5%. This is due to a workload shift rather than optimization failure: in the pre-optimization period 41% of runs processed small pull requests (ET < 100K) and 39% processed large pull requests (ET > 300K). The post-optimization period coincided with a burst of development activity, and the workflow processed 9% small pull requests and 65% large pull requests. Output tokens, which carry a 4× weight in the ET formula, rose 14% as the agent reviewed bigger diffs. The optimization likely improved per-turn efficiency, but the shift toward heavier workloads masks that gain in the aggregate numbers.

Unused tools are expensive to carry. Among the excluded gh-aw workflows, the Glossary Maintainer is an instructive case. A single tool—search_repositories—was called 342 times in one run, accounting for 58% of all tool calls, despite being completely unnecessary for a workflow that only scans local file changes. Removing it from the toolset was the optimizer’s recommendation. In gh-aw-firewall, Smoke Claude’s −79% reduction was driven in part by aggressive MCP tool pruning combined with a model-tier switch to Haiku. The Daily Community Attribution workflow illustrates the limits of this approach: it was configured with eight GitHub MCP tools and made zero calls to any of them across an entire run, but removing them did not reduce ET. Tool manifests were a small fraction of this workflow’s overall context.

A single misconfigured rule can cause runaway loops. Also among the excluded workflows, Daily Syntax Error Quality was the highest-ET workflow in the project before optimization. The root cause was a one-line misconfiguration: the workflow copied test files to /tmp/ then called gh aw compile *, but the sandbox’s bash allowlist only permitted relative-path glob patterns. Every compile attempt was blocked. Unable to use the tool it needed, the agent fell into a 64-turn fallback loop in which it manually read source code to reconstruct what the compiler would have told it. One fix to the allowed bash patterns eliminated the loop. We did not have enough baseline runs to precisely quantify the improvement, but the pathology was clear and the fix was unambiguous.

What’s next?

The tools we use to optimize our workflows including API-level observability, automated auditing workflows, MCP tool pruning, and CLI substitution are all available today in the GitHub Agentic Workflows framework. Another upcoming optimization is refactoring monolithic agents into teams of subagents using smaller and cheaper models.

The next step is to move from workflow-level optimization to system-level optimization. A workflow run is not really one flat sequence of API calls. It is a chain of episodes: short phases of work like gathering context, reading artifacts, retrying after a failure, or synthesizing a final answer. Once you can see those episodes clearly, you can ask much better questions. Which episode actually caused a costly run? Which episodes are mostly repeated work, blocked work, or failed work? Which ones should stop being agentic entirely and become deterministic pre-steps?

That same logic applies at the portfolio level. Repositories do not run one workflow in isolation. They run a fleet of agentic automations that often trigger on the same events, inspect the same diffs and logs, and produce adjacent judgments. That means cost is not just a property of a single workflow, but also of overlap across the portfolio. The next analyses we want are portfolio-level ones: where workflows are duplicating reads, where several workflows should be consolidated, and where shared intermediate artifacts should be cached instead of rediscovered by each run.

Those open questions are genuinely hard. Measuring goodput still requires outcome instrumentation that does not yet exist at scale for agentic CI workflows, and understanding episode and portfolio efficiency requires richer lineage data than most systems collect today. But that is the direction that matters. The proxy-level observability and optimizer workflows have already changed how we develop and deploy new agentic automations. We add token monitoring from day one rather than retrofitting it later, and increasingly we think in terms of avoidable work across the whole automation fleet, not just expensive runs in isolation.

If you’re running agentic workflows in CI and wondering whether you’re spending more than you need to, the first step is the same as ours: add the API proxy, turn on logging, and let the data tell you where to look.

If you want to add the workflows mentioned here, you can simply drop them into your repo using the gh-aw CLI:

gh extensions install github/gh-aw
gh aw add githubnext/agentic-ops/copilot-token-audit githubnext/agentic-ops/copilot-token-optimizer

Running them alongside your existing CI will give you immediate visibility into usage and help continuously optimize your workflows over time.

We’d love to hear how others are approaching this problem. Share your thoughts in the community discussion or join the #agentic-workflows channel of the GitHub Next Discord.

Explore the GitHub Agentic Workflows repo >

The post Improving token efficiency in GitHub Agentic Workflows appeared first on The GitHub Blog.

Read the whole story
alvinashcraft
5 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

When prompts become shells: RCE vulnerabilities in AI agent frameworks

1 Share

AI agents have fundamentally changed the threat model of AI model-based applications. By equipping these models with plugins (also called tools), your agents no longer just generate text; they now read files, search connected databases, run scripts, and perform other tasks to actively operate on your network.

Because of this, vulnerabilities in the AI layer are no longer just a content issue and are an execution risk. If an attacker can control the parameters passed into these plugins via prompt injection, the agent may be driven to perform actions beyond its intended use.

The AI model itself isn’t the issue as it’s behaving exactly as designed by parsing language into tool schemas. The vulnerability lies in how the framework and tools trust the parsed data.

To build powerful applications, developers rely heavily on frameworks like Semantic Kernel, LangChain, and CrewAI. These frameworks act as the operating system for AI agents, abstracting away complex model orchestration. But this convenience comes with a hidden cost: because these frameworks act as a ubiquitous foundational layer, a single vulnerability in how they map AI model outputs to system tools carries systemic risk.

As part of our mission to make AI systems more secure and eliminate new class of vulnerabilities, we’re launching a research series focused on identifying vulnerabilities in popular AI agent frameworks. Through responsible disclosure, we work with maintainers to ensure issues are addressed before sharing our findings with the community.

In this post, we share details on the vulnerabilities we discovered in Microsoft’s Semantic Kernel, along with the steps we took to address them and interactive way to try it yourself. Stay tuned for upcoming blogs where we’ll dive into similar vulnerabilities found in frameworks beyond the Microsoft ecosystem.

Background

We discovered a vulnerable path in Microsoft Semantic Kernel that could turn prompt injection into host-level remote code execution (RCE).

A single prompt was enough to launch calc.exe on the device running our AI agent, with no browser exploit, malicious attachment, or memory corruption bug needed. The agent simply did what it was designed to do: interpret natural language, choose a tool, and pass parameters into code.

Figure 1. Illustration of CVE-2026-26030 exploitation using a local model.

This scenario is the real security story behind modern AI agents. Once an AI model is wired to tools, prompt injection draws a thin line between being just a content security problem and becoming a code execution primitive. In this post in our research series on AI agent framework security, we show how two vulnerabilities in Semantic Kernel could allow attackers to cross that line, and what customers should do to assess exposure, patch affected agents, and investigate whether exploitation may already have occurred.

A representative case study: Semantic Kernel

Semantic Kernel is Microsoft’s open-source framework for building AI agents and integrating AI models into applications. With over 27,000 stars on GitHub, it provides essential abstractions for orchestrating AI models, managing plugins, and chaining workflows.

During our security research into the Semantic Kernel framework, we identified and disclosed two critical vulnerabilities: CVE-2026-25592 and CVE-2026-26030. These flaws, which have since been fixed, could allow an attacker to achieve unauthorized code execution by leveraging injection attacks specifically targeted at agents built within the framework.

In the following sections, we break down the mechanics of these vulnerabilities in detail and provide actionable guidance on how to harden your agents against similar exploitation.

CVE-2026-26030: In-Memory Vector Store

Exploitation of this vulnerability requires two conditions:

  1. The attacker must have a prompt injection vector, allowing influence over the agent’s inputs
  2. The targeted agent must have the Search Plugin backed by In-Memory Vector Store functionality using the default configuration

When both these two conditions are met, the vulnerability enables an attacker to achieve RCE from a prompt.

To demonstrate how this vulnerability could be exploited, we built a “hotel finder” agent  using Semantic Kernel. First, we created an In Memory Vector collection to store the hotels’ data, then exposed a search_hotels(city=…) function to the kernel (agent) so that the AI model could invoke it through tool calling.

Figure 2. Semantic Kernel agent configured with In-Memory Vector collection.

When a user inputs, for example, “Find hotels in Paris,” the AI model calls the search plugin with city=”Paris”. The plugin then first runs a deterministic filter function to narrow down the dataset and computes vector similarity (embeddings).

With this understanding of how a Semantic Kernel agent performs the search, let’s dive deep into the vulnerability.

Issue 1: Unsafe string interpolation

The default filter function that we mentioned previously is implemented as a Python lambda expression executed using eval(). In our example, The default filter will result to new_filter = “lambda x: x.city == ‘Paris'”.

Figure 3. Default filtering function definition.

The vulnerability is that kwargs[param.name] is AI model-controlled and not sanitized. This acts as a classic injection sink. By closing the quote () and appending Python logic, an attacker could turn a simple data lookup into an executable payload:

  • Input: ‘ or MALICIOUS_CODE or ‘
  • Result: lambda x: x.city == ” or MALICIOUS_CODE or ”

Issue 2: Avoidable blocklist

The framework developers anticipated this RCE risk and implemented a validator that parses the filter string into an Abstract Syntax Tree (AST) before execution.

Figure 4. Blocklist implementation.

Before running a user-provided filter code, the application runs a validation function designed to block unsafe operations. At a high level, the validation does the following:

  1. It only allows lambda expressions. It rejects outright any attempt to pass full code blocks (such as import statements or class definitions).
  2. It scans every element in the code for dangerous identifiers and attributes that could enable arbitrary code execution (for example, strings like eval, exec, open, __import__, and similar ones). If any of these identifiers appear, the code is rejected.
  3. If the code passes both checks, it is executed in a restricted environment where Python’s built-in functions (like open and print) are deliberately removed. So even if something slips through, it shouldn’t have access to dangerous capabilities.

The resulting lambda is then used to filter records in the Vector Store.

While this approach is solid in theory, blocklists in dynamic languages like Python are inherently fragile because the language’s flexibility allows restricted operations to be reintroduced through alternate syntax, libraries, or runtime evaluation.

We found a way to bypass this blocklist implementation through a specially crafted exploit prompt.

Exploit

Our exploit prompt was designed to manipulate the agent into triggering a Search Plugin invocation with an input that ultimately leads to malicious code execution:

A Malicious prompt demanding execution of the search_hotels function with the malicious argument.

This prompt circumvented the agent to trigger the following function calling:

Invocation of the “search hotels” function with the malicious argument.

As result, the lambda function was formatted as the following and executed inside eval(). This payload escaped the template string, traversed Python’s class hierarchy to locate BuiltinImporter, and used it to dynamically load os and call system(). These steps bypassed the import blocklists to launch an arbitrary shell command (for example, calc.exe) while keeping the template syntax valid with a clean closing expression.

The filter function didn’t block the payload because of the following reasons:

1. Missing dangerous names

The payload used several attributes that weren’t in the blocklist:

  • __name__  – Used to find BuiltinImporter by name
  • load_module – The method that imports modules
  • system – The method that executes shell commands
  • BuiltinImporter – The class itself

2. Structural check passes

The payload was wrapped inside a valid lambda expression. The check isinstance(tree.body, ast.Lambda) passed because the entire thing is in itself a lambda that just happens to contain malicious code in its body.

3. Empty __builtins__ is irrelevant
The eval() call used {“__builtins__”: {}} to remove access to built-in functions. However, this protection was meaningless because the payload never used built-ins directly. Instead, it started with tuple(), which exists regardless of the builtins environment, and crawled through Python’s type system to reach dangerous functionality.

4. No ast.Subscript checking
While not used in this payload, it’s worth noting that the filter only checked ast.Name and ast.Attribute nodes. If the payload needed to use a blocked name, it could’ve accessed it using bracket notation (for example, obj[‘__class__’] instead of obj.__class__), which creates an ast.Subscript node that the validation completely ignored.

Mitigation

After responsibly disclosing the vulnerability to MSRC, the Microsoft Semantic Kernel team implemented a comprehensive fix using four layers of protection to eliminate every escape primitive needed to turn a lambda filter into executable code:

  • AST node-type allowlist – Permits only safe constructs like comparisons, boolean logic, arithmetic, and literals.
  • Function call allowlist – Checks even allowed AST call nodes to ensure only safe functions can be invoked.
  • Dangerous attributes blocklist – Blocks class hierarchy traversal (for examples, __class__, __subclasses__).
  • Name node restriction – Allows only the lambda parameter (for example, x) as a bare identifier and rejects references to osevaltype, and others.
How do I know if I am affected?

Your agent is vulnerable to CVE-2026-26030 if it meets all of the following conditions:

  • It uses the Python package semantic-kernel.
  • It’s running a framework version prior to 1.39.4.
  • It uses the In-Memory Vector Store and relies on its filter functionality (when acting as the backend for the Search Plugin using default configurations).
What to do if I am affected?

You don’t need to rewrite your agent. Upgrading the Python semantic-kernel dependency to version 1.39.4 or higher mitigates the risk.

What about the time that my agent was vulnerable?

While patching closes the bug, but it doesn’t answer the retrospective question defenders care about: whether their agent was exploited before they upgraded.

First, define the vulnerable window for each affected deployment: from the moment a vulnerable Semantic Kernel Python version was deployed until the moment version 1.39.4 or later was installed. Any investigation should focus on that time range.

Second, hunt for host-level post-exploitation signals during that vulnerable window. Because successful exploitation results in code execution on the host, the most useful evidence is in endpoint telemetry: suspicious child processes, outbound connections, or persistence artifacts created by the agent host process. We provide a set of practical advanced hunting queries for further investigation in a separate section of this blog.

If you find suspicious activity during that window, treat it as a potential host compromise. Review the affected host, rotate credentials and tokens accessible to the agent, and investigate what data or systems that host could reach.

CVE-2026-25592: Arbitrary file write through SessionsPythonPlugin

Before diving into the mechanics of this second vulnerability, here is what an agent sandbox escape looks like in practice: with a single prompt, an attacker could bypass a cloud-hosted sandbox, write a malicious payload directly to the host device’s Windows Startup folder, and achieve full RCE.

The container boundary

Semantic Kernel includes a built-in plugin called SessionsPythonPlugin that allows agents to safely execute Python code inside Azure Container Apps dynamic sessions, which are isolated cloud hosted sandboxes with their own filesystem.

The security model relies entirely on this boundary. Code runs in the isolated sandbox and cannot touch the host device where the agent process runs. To help move data in and out of the sandbox, the plugin uses helper functions like UploadFile and DownloadFile, which run on the host side to transfer files across this boundary.

The vulnerability

In the .NET software development kit (SDK), DownloadFileAsync was accidentally marked with a [KernelFunction] attribute, which officially advertised it to the AI model as a callable tool, complete with its parameter schema:

Because of this attribute, the localFilePath parameter, which dictates exactly where File.WriteAllBytes() saves data on the host device, was now entirely AI controlled. With no path validation, directory restriction, or sanitization in place, an attacker wouldn’t need a complex hypervisor exploit; they just needed to prompt the model to do it for them.

(Note: Arbitrary File Read. A similar vulnerability existed in reverse for the upload_file() function across both the Python and .NET SDKs. It accepted any local file path without validation, allowing prompt injections to exfiltrate sensitive host files, like SSH keys or credentials, directly into the sandbox).

Attack chain overview

By chaining two exposed tools, an attacker could turn standard function calling into a sandbox escape:

Step 1: Create the payload

An  injected prompt instructs the agent to use the ExecuteCode tool to generate a malicious script inside the isolated container:

At this point, the payload is contained. It exists only in the sandbox and cannot execute on the host.

Step 2: Escape the sandbox

A second injected instruction tells the AI model to use the DownloadFileAsync tool to download the file to a dangerous location on the host:

The agent calls:

The agent fetches the script from the sandbox’s API and writes it directly to the host’s Windows\Start Menu\Programs\Startup folder.

Step 3: Execute the code

On the next user sign-in, the script runs, granting full host compromise.

This exploit illustrates the MITRE ATLAS technique AML.T0051 (LLM Prompt Injection) cascading into AML.T0016 (Obtain Capabilities).

Exposing DownloadFileAsync provided a direct file write primitive on the host filesystem, effectively negating the container isolation.

The fix and how to defend

Semantic Kernel patched this vulnerability by removing the root cause of tool exposure and adding defense in depth:

Removed AI access – The [KernelFunction] attribute was removed, making the function invisible to the AI model. The AI agent can no longer invoke it, and prompt injection can no longer reach it:

This single change breaks the entire attack chain. The AI can now only be called directly by the developer’s intentional code.

  • Path validation – For developers calling the function programmatically, a ValidateLocalPathForDownload() method was added using path canonicalization (Path.GetFullPath()) and directory allowlist matching to ensure the target path falls within permitted directories:
Similar opt-in protections were applied to uploads.
How do I know if I am affected?

Your agent is vulnerable to CVE-2026-25592 if it uses a Semantic Kernel .NET SDK version older than 1.71.0.

Defending the agentic edge

If you use Semantic Kernel, our primary recommendation is to upgrade immediately. You don’t need to rewrite your agent’s architecture; the security updates simply remove the AI model’s ability to trigger these functions autonomously.

More broadly, defending AI agents requires acknowledging that AI models aren’t security boundaries. Security teams must correlate signals across two layers: the AI model level (intent detection through meta prompts and content safety filters) and the host level (execution detection). If an attacker bypasses the AI model guardrails, traditional endpoint defense must be in place to detect anomalous behavior, such as an AI agent process suddenly spawning command lines or dropping scripts into Startup folders.

Not bugs, but developed by design

Untrusted data being used as input for high-risk operations isn’t entirely new. In the early days of web application security, such input was passed directly into SQL queries or filesystem APIs. Today, agents are doing something similar, in that they could map untrusted natural-language input to system tools.

The overarching lesson from both vulnerabilities is that both aren’t bugs in the AI model itself, but rather issues in agent architecture and tool design. We must make a clear distinction between model behavior and agent architecture. The AI model functions exactly as it was designed to: translate intent into structured tool calls.

When models are connected to system tools, prompt injection risks may extend beyond typical chatbot misuse and require additional safeguards. Instead, it becomes a direct path to concrete execution primitives like data exfiltration, arbitrary file writes, and RCE. For a deeper look at the runtime risks of tool-connected AI models, see Running OpenClaw safely: identity, isolation, and runtime risk.

As mentioned previously, your LLM is not a security boundary. The tools you expose define your attacker’s affected scope. Any tool parameter the model can influence must be treated as attacker-controlled input.

In the next blog in this series, we’ll expand beyond Semantic Kernel to explore structurally similar execution vulnerabilities that we found in other widely used third-party agent frameworks.


CTF challenge: Attack your own agent

If you want to see how prompt injections escalate into execution and to put your skills to the test, we’ve packaged the vulnerable hotel-finder agent that we described in this blog into an interactive, hands-on capture-the-flag (CTF) challenge.

This CTF challenge lets you step into the shoes of an attacker and try to exploit the CVE-2026-26030 vulnerability in a controlled environment. You need to craft a prompt injection that not only bypasses the agent’s natural language defenses but also smuggle a Python AST-traversal payload through the vulnerable eval() sink.

To see if you can manipulate the AI model into launching arbitrary code and popping calc.exe on the server, download the challenge, spin it up in a sandbox, and see if you can achieve RCE. Keep in mind that this challenge is for educational purposes only, and shouldn’t be run in production environments.

Reconnaissance:

Exploit (jailbreak and payload):

Note: Because the agent will running locally on your device, calc.exe will open on your desktop. In a real-world scenario, such an executable file will launch remotely on the server hosting the agent.

Download the CTF challenge: https://github.com/amiteliahu/AIAgentCTF/tree/main/CVE-2026-26030

Advanced hunting

The following advanced hunting queries lets you surface suspicious activities from Semantic Kernel agents.

Detect common RCE post-exploitation child processes from Semantic Kernel agent hosts

DeviceProcessEvents
| where Timestamp > ago(30d)
| where InitiatingProcessCommandLine matches regex @"(?i)semantic[\s_\-]?kernel"
    or InitiatingProcessFolderPath matches regex @"(?i)semantic[\s_\-]?kernel"
| where FileName in~ (
    "cmd.exe", "powershell.exe", "pwsh.exe", "bash.exe", "wsl.exe",
    "certutil.exe", "mshta.exe", "rundll32.exe", "regsvr32.exe",
    "wscript.exe", "cscript.exe", "bitsadmin.exe", "curl.exe",
    "wget.exe", "whoami.exe", "net.exe", "net1.exe", "nltest.exe",
    "klist.exe", "dsquery.exe", "nslookup.exe"
)
| project 
    Timestamp,
    DeviceName,
    AccountName,
    FileName,
    ProcessCommandLine,
    InitiatingProcessFileName,
    InitiatingProcessCommandLine,
    InitiatingProcessFolderPath
| sort by Timestamp desc

Detect .NET hosting Semantic Kernel that spawns suspicious children

DeviceProcessEvents
| where Timestamp > ago(30d)
| where InitiatingProcessFileName in~ ("dotnet.exe")
| where InitiatingProcessCommandLine matches regex @"(?i)(semantic[\s_\-]?kernel|SKAgent|kernel\.run)"
| where FileName in~ (
    "cmd.exe", "powershell.exe", "pwsh.exe", "bash.exe",
    "certutil.exe", "curl.exe", "whoami.exe", "net.exe"
)
| project 
    Timestamp,
    DeviceName,
    AccountName,
    FileName,
    ProcessCommandLine,
    InitiatingProcessFileName,
    InitiatingProcessCommandLine
| sort by Timestamp desc

Learn more

For the latest security research from the Microsoft Threat Intelligence community, check out the Microsoft Threat Intelligence Blog.

To get notified about new publications and to join discussions on social media, follow us on LinkedInX (formerly Twitter), and Bluesky.

To hear stories and insights from the Microsoft Threat Intelligence community about the ever-evolving threat landscape, listen to the Microsoft Threat Intelligence podcast.

Review our documentation to learn more about our real-time protection capabilities and see how to enable them within your organization.   

The post When prompts become shells: RCE vulnerabilities in AI agent frameworks appeared first on Microsoft Security Blog.

Read the whole story
alvinashcraft
5 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Surprise Elon-Anthropic Team Up Reshapes AI Race

1 Share
From: AIDailyBrief
Duration: 26:40
Views: 300

Surprise SpaceX–Anthropic partnership injects massive Colossus-scale compute into Anthropic's Claude ecosystem and reframes compute as a strategic kingmaker. DevDay emphasized an agent-first strategy with Dreaming (scheduled memory review), Managed Agents, Outcomes-based grading, add-ins, industry connectors, and multi-agent orchestration. Shift toward harnesses and agent ecosystems raises business and safety questions about consolidation, continual learning, and control of critical AI infrastructure.

The AI Daily Brief helps you understand the most important news and discussions in AI.
Subscribe to the podcast version of The AI Daily Brief wherever you listen: https://pod.link/1680633614
Get it ad free at http://patreon.com/aidailybrief
Learn more about the show https://aidailybrief.ai/

Read the whole story
alvinashcraft
5 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Security Dashboard for AI: 3 Ways CISOs Drive Impact Today

1 Share

AI is reshaping the enterprise and, with it, the threat landscape. Today's organizations face new threats with AI agents that modify configurations, execute workflows, and access data without direct human oversight. As a result, the gap between AI adoption and AI governance is widening, and CISOs face growing challenges to maintain visibility, control, and compliance across an increasingly complex ecosystem.

As AI becomes embedded across the enterprise, CISOs face four key challenges:

  • Scale without visibility: Over 75% of enterprises surveyed by PWC report they are already adopting AI agents. ¹At the same time, over 80% of security teams surveyed by Nokod report visibility gaps into the applications and AI agents created within their organization. ²Rapid AI proliferation and evolving regulations make unified visibility across AI platforms, apps, and agents critical for CISOs.
  • Fragmentation: Organizations rely on multiple siloed tools for AI asset visibility, making oversight fragmented and inefficient. According to Gartner’s 2024 survey of 162 enterprises, organizations use 45 cybersecurity tools on average.
  • Expanding AI risk: AI proliferation is rapidly increasing the attack and risk surface, with the surge of AI-generated identities. By 2027, 4 out of 5 organizations will face phishing attacks powered by AI-generated synthetic identities, according to IDC. ³This makes it harder for CISOs to track emerging threats, unmanaged assets, and shifting risk patterns.
  • Overload: Alert fatigue is now a top challenge, with organizations now receiving an average of 2,992 security alerts daily, yet 63% go unaddressed. Increasing AI risk without a way to prioritize what matters most compounds pressure on CISOs.

In conversations between Microsoft and CISOs, one common need emerged: a single place to view integrated AI risk across the enterprise. To address these growing challenges, we are excited to provide CISOs with the Security Dashboard for AI, which recently became generally available. This unified dashboard aggregates posture and real-time risk signals from Microsoft Defender, Entra, and Purview into one unified, executive-level view of AI posture, risk, and inventory across agents, apps, and platforms.

The Security Dashboard for AI helps CISOs:

  • Gain unified AI risk visibility: Discover AI agents and applications and continuously monitor posture across the environment
  • Prioritize critical risks: Correlate signals across identity, data, and threat protection to surface the most urgent issues
  • Drive risk mitigations: Investigate activity and take action to help reduce exposure across the AI ecosystem

The dashboard is capable of aggregating and surfacing AI risks from across Microsoft Defender, Entra, Purview - including Microsoft 365 Copilot, Microsoft Copilot Studio agents, and Microsoft Foundry applications and agents as well as cross-platform AI risks with Microsoft network-based or SDK-enabled integrations, and MCP servers. This supports comprehensive visibility and control, regardless of where applications and agents are built. As you activate Microsoft Security for AI capabilities, you can gain richer visibility into different aspects of your AI risk posture.

 

Figure 1: Security Dashboard for AI in browser

Getting Started with the Security Dashboard for AI

The Security Dashboard for AI is provided at no additional cost to customers already using Defender, Entra, and/or Purview to protect their AI innovation. Based on how early adopter CISOs are using the dashboard, here are three ways you can start leveraging the dashboard today.

1. Manage Daily AI Risk 

Beyond reporting, you must stay hands-on with AI risks, scanning for emerging issues, verifying asset governance, and delegating remediations. The Security Dashboard for AI consolidates daily operations into a single pane of glass, surfacing critical alerts, unmanaged assets, and emerging risks. Use the dashboard as a daily AI risk radar, enabling rapid triage and ensuring you focus on the most urgent threats.

  • Scan and triage daily AI risk: Start each day by identifying and prioritizing the highest-risk AI exposures. Risks are prioritized on severity reported by underlying security tools, helping you focus on the most critical exposures.
  • Track AI asset inventory and monitor agent sprawl: Use the Inventory page to gain comprehensive visibility into all AI assets. Identify newly registered assets to mitigate the risk of shadow or unmanaged IT and surface inactive agents to proactively monitor and control agent sprawl.
  • Delegate tasks for remediation: Move from insight to action by delegating tasks to your security team with easy click delegation. Delegation routes ownership via email or Microsoft Teams with notifications, due date, and ownership tracking. Delegate actions to specific roles such as global admin and AI administrator, without granting full access to underlying tools.

 

Figure 2: Security Dashboard for AI risk page

2. Guide Briefings with Security Teams

You require up-to-date intelligence to guide conversations with Security Teams about what is happening across the AI estate. The Security Dashboard for AI helps you anchor discussions in specific risks, trends, and ownership gaps surfaced in the data. The dashboard becomes a conversation driver, helping you ask the right questions about risk and security posture, to help ensure you and your team are triaging the right priorities. Because the dashboard consolidates signals from Defender, Entra, and Purview, both CISO and security teams operate from the same facts, enabling more outcome-driven discussions and faster prioritization, so you can shift the conversations from status updates to targeted action planning.

  • Prioritize top AI Risk: Use the dashboard to help you prioritize the AI risk that matters the most. In preparation for team meetings, use Microsoft Security Copilot to explore AI risks, agent activity, and security recommendations via prompts to strengthen your AI security posture. With your team, take a closer look at risk vectors like data leakage, oversharing and unethical behavior, and discuss what actions need to be taken.
  • Review Security Recommendations: Create a routine with your security team to review the recommended Microsoft security actions and track your progress over time. Across regular team check‑ins, review what has been addressed, what remains open, and which actions require follow‑up so you are prepared to respond to regulatory, audit, or executive questions with up‑to‑date metrics.

 

Figure 3: Security Dashboard for AI inventory page

Figure 4: Security Dashboard for AI delegation

3. Executive Reporting

Reporting to the board on AI security posture has historically meant weeks of manual data gathering across multiple tools. The Security Dashboard for AI streamlines the data collection process with a single source of truth for AI risk, enabling confident, data-backed insights for your board presentations and conversations. Early adopters confirm the value and are using it for quarterly executive briefings.

  • Prepare for Board Discussions: Use the dashboard to help get the right insights at the right altitude to help you prepare for discussions with your board. The Overview page aggregates identity, data security, and threat protection signals from Defender, Entra, and Purview into an AI risk scorecard with risk factors. The embedded Security Copilot AI-powered insights provide suggested prompts with risk assessments, summaries, and recommendations to help you prioritize what matters most.
  • Extend Observability to Executive Stakeholders: Authorize AI risk follow‑ups to the appropriate security, identity, or governance owners using Microsoft Teams or email. Distribute visibility across GRC lead, AI governance, and IT leaders, while maintaining executive‑level oversight.

 

Figure 5: Security Dashboard for AI Copilot prompt gallery

Next Steps

The Security Dashboard for AI helps CISOs manage AI risk faster, more confidently and more collaboratively with their team. Defender, Entra, and Purview signals are surfaced in a single pane of glass, providing observability across your AI estate. Drive faster triage, use data to support board-level discussions about AI risk, and enable coordinated action with integrated insights, recommendations, and delegation to help accelerate remediation across existing security workflows.

The Security Dashboard for AI is generally available now. If your organization uses Microsoft Defender, Entra, and/or Purview, you already have access, no additional licensing is required.

 

¹AI agent survey. PwC, May 2025
²Security Teams Taking on Expanded AI Data Responsibilities. Bedrock Data, March 2025
³IDC FutureScape: Worldwide Security and Trust 2026 Predictions, November 2025
⁴2026 State of Threat Detection and Response Report. Vectra AI, February 2026

Read the whole story
alvinashcraft
6 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

New in the Postman API v1.39: API Catalog and Spec Hub endpoints

1 Share

If you’ve been using API Catalog to monitor your services or Postman Spec Hub to manage your API specifications, you’ve probably wished you could pull that data into your own tooling. With the Postman API collection v1.39.0, now you can.

This release adds 8 new endpoints across 2 feature areas: API Catalog services and Spec Hub version tags. I’ll walk through what each one does, show you the requests, and share a few patterns I’ve found useful for putting them to work.

What’s in the release

Here’s the full list of new endpoints:

API Catalog

Method Endpoint What it returns
GET /api-catalog/services List of services in a system environment with analytics, compliance, and governance metadata
GET /api-catalog/services/{serviceId} Details for a single service, including health, traffic, compliance, ownership, and dependencies
GET /api-catalog/services/{serviceId}/endpoints Observed API endpoints for a service with performance metrics
GET /api-catalog/services/{serviceId}/monitor-runs Scheduled monitor runs with summary statistics
GET /api-catalog/services/{serviceId}/spec-lints API specification lint runs with summary statistics and per-severity issue counts
GET /api-catalog/services/{serviceId}/ci-runs CI collection runs with summary statistics, pipeline details, and Git metadata

Spec Hub

Method Endpoint What it returns
GET /specs/{specId}/version-tags List of a specification’s version tags
GET /specs/{specId}/version-tags/{tagId}/files Snapshot of a specification at the tagged point in time
POST /specs/{specId}/version-tags Creates a new version tag for a specification

API Catalog: query your services programmatically

API Catalog gives you a centralized view of every service in your organization. It aggregates traffic analytics, compliance signals from monitor runs and CI pipelines, specification lint results, and governance metadata into a single dashboard. The new endpoints open all of that data up to automation.

List your services

Start by pulling the full list of services in your system environment:

GET {{baseUrl}}/api-catalog/services
X-API-Key: {{postman-api-key}}

The response includes each service’s analytics, compliance status, and governance metadata:

{
  "services": [
    {
      "id": "svc_a1b2c3",
      "name": "payments-api",
      "workspace": {
        "id": "ws_d4e5f6",
        "name": "Payments Team"
      },
      "analytics": {
        "totalEndpoints": 24,
        "requestVolume7d": 1482300,
        "p95Latency": 142
      },
      "compliance": {
        "monitorStatus": "passing",
        "ciStatus": "passing",
        "specLintScore": 94
      },
      "governance": {
        "governanceGroup": "public-apis",
        "specVersion": "OpenAPI 3.1"
      }
    }
  ]
}

Drill into a service

Once you have a service ID, you can pull the full picture, including health, traffic, ownership, and dependency data:

GET {{baseUrl}}/api-catalog/services/svc_a1b2c3
X-API-Key: {{postman-api-key}}
{
  "id": "svc_a1b2c3",
  "name": "payments-api",
  "health": {
    "status": "healthy",
    "uptimePercent": 99.97,
    "errorRate4xx": 0.3,
    "errorRate5xx": 0.01
  },
  "traffic": {
    "requestVolume24h": 211758,
    "p50Latency": 45,
    "p95Latency": 142,
    "p99Latency": 310
  },
  "ownership": {
    "team": "Payments Team",
    "workspace": "ws_d4e5f6"
  },
  "dependencies": [
    { "serviceId": "svc_x7y8z9", "name": "auth-service" },
    { "serviceId": "svc_m1n2o3", "name": "notifications-api" }
  ]
}

This is the kind of data I’d want feeding into a weekly API health report or a Slack alert when error rates spike.

Check endpoint performance

The /endpoints subresource returns the observed API endpoints for a service along with their performance metrics:

GET {{baseUrl}}/api-catalog/services/svc_a1b2c3/endpoints
X-API-Key: {{postman-api-key}}
{
  "endpoints": [
    {
      "method": "POST",
      "path": "/v1/payments",
      "requestVolume7d": 342100,
      "p95Latency": 189,
      "errorRate": 0.02
    },
    {
      "method": "GET",
      "path": "/v1/payments/{paymentId}",
      "requestVolume7d": 890400,
      "p95Latency": 67,
      "errorRate": 0.001
    }
  ]
}

Pull compliance signals

The compliance-related endpoints give you granular access to monitor runs, specification lint results, and CI pipeline data. Here’s how to check recent specification lint runs:

GET {{baseUrl}}/api-catalog/services/svc_a1b2c3/spec-lints
X-API-Key: {{postman-api-key}}
{
  "specLints": [
    {
      "id": "lint_r4s5t6",
      "runAt": "2026-05-03T14:22:00Z",
      "status": "completed",
      "summary": {
        "totalIssues": 3,
        "errors": 0,
        "warnings": 2,
        "info": 1
      }
    }
  ]
}

And CI collection runs, which include pipeline details and Git metadata:

GET {{baseUrl}}/api-catalog/services/svc_a1b2c3/ci-runs
X-API-Key: {{postman-api-key}}
{
  "ciRuns": [
    {
      "id": "ci_u7v8w9",
      "runAt": "2026-05-03T15:30:00Z",
      "status": "passed",
      "summary": {
        "totalTests": 48,
        "passed": 48,
        "failed": 0
      },
      "pipeline": {
        "provider": "github-actions",
        "workflow": "api-tests.yml"
      },
      "git": {
        "branch": "main",
        "commitSha": "a1b2c3d"
      }
    }
  ]
}

Spec Hub: manage version tags through the API

Spec Hub is where you design, standardize, and govern your API specifications. It supports OpenAPI (2.0, 3.0, and 3.1), AsyncAPI 2.0, protobuf (versions 2 and 3), and GraphQL.

Version tags are snapshots of a specification at a point in time. Think of them like Git tags for your API design. They let you track how your specifications evolve, compare changes between releases, and pin integrations to a known-good version.

List version tags

Pull the tags for a specification to see its version history:

GET {{baseUrl}}/specs/spec_j1k2l3/version-tags
X-API-Key: {{postman-api-key}}
{
  "versionTags": [
    {
      "id": "tag_p4q5r6",
      "name": "v2.1.0",
      "createdAt": "2026-04-28T10:00:00Z",
      "createdBy": "usr_abc123"
    },
    {
      "id": "tag_s7t8u9",
      "name": "v2.0.0",
      "createdAt": "2026-03-15T09:30:00Z",
      "createdBy": "usr_abc123"
    }
  ]
}

Get a version tag snapshot

Retrieve the files for a specific version tag to see the specification as it existed at that point:

GET {{baseUrl}}/specs/spec_j1k2l3/version-tags/tag_p4q5r6/files
X-API-Key: {{postman-api-key}}
{
  "tag": {
    "id": "tag_p4q5r6",
    "name": "v2.1.0",
    "createdAt": "2026-04-28T10:00:00Z"
  },
  "files": [
    {
      "path": "openapi.yaml",
      "content": "openapi: 3.1.0\ninfo:\n  title: Payments API\n  version: 2.1.0\npaths:\n  /v1/payments:\n    post:\n      summary: Create a payment\n..."
    },
    {
      "path": "components/schemas/Payment.yaml",
      "content": "type: object\nproperties:\n  id:\n    type: string\n  amount:\n    type: number\n..."
    }
  ]
}

This is useful for diffing specification versions in your own tooling or for building automation that validates whether a deployed API matches its tagged specification.

Create a version tag

Tag a specification to freeze its current state:

POST {{baseUrl}}/specs/spec_j1k2l3/version-tags
X-API-Key: {{postman-api-key}}
Content-Type: application/json

{
  "name": "v2.2.0"
}
{
  "id": "tag_v1w2x3",
  "name": "v2.2.0",
  "createdAt": "2026-05-04T12:00:00Z",
  "createdBy": "usr_abc123"
}

I’ve started tagging specifications as part of my release process. The tag goes out right before we merge to main so there’s always a snapshot tied to what went to production.

Patterns I’ve found useful

Build an API health dashboard

Combine the services list with the endpoints and compliance data to build a dashboard that shows your team what matters:

const apiKey = pm.environment.get("postman-api-key");
const baseUrl = pm.environment.get("baseUrl");

const servicesResponse = await pm.sendRequest({
    url: `${baseUrl}/api-catalog/services`,
    method: "GET",
    header: { "X-API-Key": apiKey }
});

const services = servicesResponse.json().services;

for (const service of services) {
    const endpointsResponse = await pm.sendRequest({
        url: `${baseUrl}/api-catalog/services/${service.id}/endpoints`,
        method: "GET",
        header: { "X-API-Key": apiKey }
    });

    const slowEndpoints = endpointsResponse.json().endpoints
        .filter(ep => ep.p95Latency > 200);

    if (slowEndpoints.length > 0) {
        console.log(`${service.name}: ${slowEndpoints.length} endpoints above 200ms p95`);
    }
}

Automate specification tagging in CI/CD

Add specification tagging to your release pipeline so every deployment has a corresponding version snapshot. Here’s a GitHub Actions step using the Postman CLI:

- name: Tag API specification
  run: |
    curl -s -X POST \
      "https://api.postman.com/specs/${{ secrets.SPEC_ID }}/version-tags" \
      -H "X-API-Key: ${{ secrets.POSTMAN_API_KEY }}" \
      -H "Content-Type: application/json" \
      -d '{"name": "v${{ github.ref_name }}"}'
  env:
    POSTMAN_API_KEY: ${{ secrets.POSTMAN_API_KEY }}

Monitor specification compliance across services

Pull lint results for all your services and flag anything with errors:

const services = servicesResponse.json().services;

for (const service of services) {
    const lintsResponse = await pm.sendRequest({
        url: `${baseUrl}/api-catalog/services/${service.id}/spec-lints`,
        method: "GET",
        header: { "X-API-Key": apiKey }
    });

    const latestLint = lintsResponse.json().specLints[0];
    if (latestLint && latestLint.summary.errors > 0) {
        console.log(
            `${service.name}: ${latestLint.summary.errors} spec lint errors`
        );
    }
}

Things to watch for

Rate limits still apply. If you’re iterating over dozens of services and calling multiple sub-endpoints per service, you’ll want to add some pacing. I batch my requests in groups of 5 with a short delay between batches.

Service IDs are environment-scoped. The same service can have different IDs across system environments. Make sure you’re querying the right environment when building cross-environment comparisons.

Version tags are immutable. Once you create a tag, you can’t change it. If you need to correct something, create a new tag with an updated name. This is by design since immutability is what makes tags trustworthy as release markers.

What this means for your workflow

These endpoints close the gap between what you can see in Postman’s dashboard and what you can automate. If you’ve been manually checking API Catalog for compliance status or hand-tagging specifications before releases, you can now script those workflows.

The API Catalog endpoints are particularly valuable for platform engineering teams building internal developer portals. Instead of asking developers to check Postman for service health, you can surface that data wherever your team already works.

The Spec Hub version tag endpoints fit naturally into API-first development workflows. Tag your specifications at every release, compare them programmatically, and catch breaking changes before they reach production.

Import the updated Postman API collection into your workspace and try the new endpoints against your own services. Start with GET /api-catalog/services to see what data is available, then build from there.

Resources

The post New in the Postman API v1.39: API Catalog and Spec Hub endpoints appeared first on Postman Blog.

Read the whole story
alvinashcraft
6 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Least privilege AI agents: A new azd template from Curity and Microsoft

1 Share

If you ever built an AI agent demo, you probably had this moment. Everything works: the agent interprets natural language, calls the right tools, and returns the right data. Then you start designing for the real users of the app. You think about data boundaries: what if someone tries to get data they’re not supposed to see?

For example, imagine a customer support app where users can say “give me a markdown report on the last three months of stock transactions and the value of my portfolio.” Then an agent does the work. First, you need to design for security so the agent doesn’t commingle data from separate customers. What stops the agent from showing one customer another customer’s portfolio?

This scenario is a design question we need to answer now. We spoke with the Curity team about how they think about security and they had a template in mind for building agents. This post walks through what’s in it and why we made the choices we did.

What the template gives you

The new azd template deploys a full AI agent app to Azure. You get:

  • A backend agent on Microsoft Foundry, built in C# with the Microsoft A2A and MCP SDKs.
  • An MCP server that exposes a sample portfolio API.
  • The Curity Identity Server configured as an authorization server, working alongside Microsoft Entra ID for user authentication.
  • External and internal API gateways that handle token exchange and audit logging.
  • Bicep that provisions the Azure infrastructure (Container Apps, virtual network, container registry, Azure AI Foundry resource, Key Vault, Azure SQL Database, and storage for config files).

A few azd commands and it’s all running in your subscription. From there, you can explore further and customize it to your customer scenarios.

Why put authorization in the template

It’s easy to confuse authentication with authorization. The user’s identity doesn’t tell you what data they should see. Most agent samples handle the first part well, but they leave a real question unanswered: when an agent calls your API on behalf of a user, how does the API know what data this user is allowed to see?

This scenario matters more for agents than it does for traditional clients. A regular client app makes predictable API calls. An AI agent is nondeterministic. It interprets natural language and decides what to call. It can be creative. It can also be wrong. And if someone tricks it through prompt injection, you need rules that don’t depend on the AI being perfect.

We like that this template shows a pattern that holds up against creative agents, mistakes, and prompt injection beyond the happy path.

The pattern: Short-lived tokens with the right values inside

The template uses OAuth 2.0 access tokens. Agents never get permanent access to the APIs. They get short-lived tokens that carry exactly the information the API needs to make a decision.

Here’s what one of those tokens looks like at the Portfolio MCP Server. It’s the third token in the chain, and two token exchanges produce it. The first exchange narrows the scope of the user’s initial token and converts it from an opaque token to a JSON Web Token (JWT). The second exchange adds the agent identity and a new audience for the MCP server hop.

{
  "jti": "fc7fbe2a-27d1-4a95-ab05-5a95bd236a07",
  "delegationId": "2818695a-949a-4622-b5cb-9c1a9ba49716",
  "exp": 1771434954,
  "nbf": 1771434054,
  "scope": "stocks/read",
  "iss": "http://localhost:8443/oauth/v2/oauth-anonymous",
  "sub": "62c839b8214aa1fe8cbcd823948a4bc705fbbba69c7666e334ee5c7fb348b60a",
  "aud": "https://mcp.demo.example",
  "iat": 1771434054,
  "purpose": "access_token",
  "customer_id": "178",
  "region": "USA",
  "client_id": "console-client",
  "client_type": "ai-agent",
  "agent_id": "autonomous-agent"
}

scope is stocks/read (read-only). customer_id is 178, which comes from an Entra ID attribute. region is USA, also from Entra ID, and restricts which stocks the user can see. client_type is ai-agent, which lets the API tighten authorization specifically when an agent is in the call chain. The MCP server uses these values to filter the data it returns. So no matter what the agent decides to do, it can only see stocks for customer 178 in the USA region.

For developers, the token-driven filtering is the part we like the most: the API code stays clean. The MCP server pulls values out of the token and uses them in its queries. There’s no custom authorization logic to write. The hard work happens upstream, where someone designs the token in the first place.

Gary and the team at Curity have designed tokens for years. Much of the template came from that expertise: which values to put in, what scopes to define, and how to think about least privilege. For their take from the identity side, see Gary’s post on the Curity blog.

What’s interesting about the deployment

The template uses a layered Bicep approach:

  • A base layer with networking, the container registry, and shared services.
  • An identity layer with the Curity Identity Server and the API gateways.
  • An applications layer with the agent and the MCP server.

Everything runs on an internal Container Apps network, and the template exposes only the endpoints that need to be public. An external gateway sits in front of the agent and handles token exchange so internet clients never see the rich JWTs that flow internally. An internal gateway sits between the agent and the MCP server, where it writes audit logs and enforces coarse rules. For example, blocking certain scopes from agent-issued tokens. Most teams already deploy APIs and services on Azure in this fashion, so the pattern should feel familiar to your platform team.

The Curity Identity Server runs in containers in Azure that the template pulls from Curity’s container registry. (Outside this template, you can also deploy Curity from the Microsoft Marketplace if that fits your procurement story better.) Entra ID handles user sign-in and identity storage. You’re not replacing Entra ID. You’re adding a token issuer next to it that gives the agents the short-lived, narrowly scoped credentials they need.

Where to take it from here

The basic template gets you a working agent with proper authorization. From there, the more interesting scenarios are the ones we’re seeing real teams ask about:

  • Human approval in the loop. A user says “buy 1,000 shares of CONTOSO when the price drops below $50.” When the price hits, the agent pauses for a human to confirm before getting a token with enough scope (say, stocks/write) to place the trade.
  • Working across organizations. An agent at one company calls an agent at a partner company, passing along the right context about who the user is and what they’re allowed to do.

Both of these scenarios are token design problems more than they’re AI problems, which is why we wanted a template that puts authorization front and center.

Try it

The template is on GitHub: curityio/azd-ai-autonomous-agent. The README walks you through prerequisites, local-first quickstart, and the layered Azure deployment.

A few tips before you start:

  • Run it locally first. The template ships with a local end-to-end flow (./tools/local/backend.sh) that runs everything in Docker. It’s the fastest way to see the token exchange in action before you deploy to Azure.
  • Pick a region that supports gpt-4.1-mini. East US 2, Sweden Central, and UK South are good choices. Check region availability before you start.
  • Use dev as the environment name when azd init prompts you. The deployment scripts and audit-log commands in the README assume it.
  • Watch tokens flow. Tail the internal gateway’s audit logs (az containerapp logs show --resource-group rg-dev --name gateway-internal-dev --follow) to see scope, customer_id, region, and agent_id on every call.

If you’d like the deeper identity perspective on the same template, Gary’s post on the Curity blog covers the token design side in more detail. And if you build something with the template, open an issue on the template repo and tell us about it.

Additional resources

The post Least privilege AI agents: A new azd template from Curity and Microsoft appeared first on Azure SDK Blog.

Read the whole story
alvinashcraft
6 minutes ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories