<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: With Nate</title>
    <description>The latest articles on DEV Community by With Nate (@nasdigital).</description>
    <link>https://dev.to/nasdigital</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3950619%2F1705f039-c7e6-4d01-abe0-518a76139b6b.png</url>
      <title>DEV Community: With Nate</title>
      <link>https://dev.to/nasdigital</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nasdigital"/>
    <language>en</language>
    <item>
      <title>Level Up Your Game Assets: 40 Characters, Zero Restyling</title>
      <dc:creator>With Nate</dc:creator>
      <pubDate>Sun, 26 Jul 2026 02:55:12 +0000</pubDate>
      <link>https://dev.to/nasdigital/level-up-your-game-assets-40-characters-zero-restyling-186o</link>
      <guid>https://dev.to/nasdigital/level-up-your-game-assets-40-characters-zero-restyling-186o</guid>
      <description>&lt;p&gt;If you've bought a character asset pack before, you know the drill: the art is fine, but the characters don't fit &lt;em&gt;your&lt;/em&gt; game. No lore, no stats, no sense of who these people are. You end up inventing all of that yourself, which is the part that actually takes time.&lt;/p&gt;

&lt;p&gt;I built the &lt;strong&gt;Ultimate Character Collection&lt;/strong&gt; to ship the documentation alongside the art.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's actually in it
&lt;/h2&gt;

&lt;p&gt;40 characters across four packs, each with its own setting:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pack&lt;/th&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Style&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Vol.01 — Neon Future&lt;/td&gt;
&lt;td&gt;Cyberpunk&lt;/td&gt;
&lt;td&gt;Sleek sci-fi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vol.02 — Old Kingdom&lt;/td&gt;
&lt;td&gt;Medieval fantasy&lt;/td&gt;
&lt;td&gt;Gothic-anime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vol.03 — Ten Outlines&lt;/td&gt;
&lt;td&gt;Modern urban&lt;/td&gt;
&lt;td&gt;Gritty contemporary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vol.04 — Broken World&lt;/td&gt;
&lt;td&gt;Post-apocalyptic&lt;/td&gt;
&lt;td&gt;Painterly-anime&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Per character:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;4 facial expressions (neutral, confident, intense, concerned) — same pose, same outfit, same lighting, only the face changes, so they work as a dialogue UI set&lt;/li&gt;
&lt;li&gt;4 portrait sizes: full (307×512), clean (no title overlay, for in-engine UI), 2× HD upscale (614×790), and a bust shot (512×341) for chat heads&lt;/li&gt;
&lt;li&gt;Square avatar (512×512)&lt;/li&gt;
&lt;li&gt;A stat card (800×1000)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Per pack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A 14-page Character Bible PDF — lore, stats, weapons, playstyle, and dev notes&lt;/li&gt;
&lt;li&gt;Lineup poster (1600×980) and a character-select UI mockup (1600×900)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;characters.json&lt;/code&gt; — machine-readable, so you can load the roster programmatically&lt;/li&gt;
&lt;li&gt;Ren'Py demo scripts with working dialogue you can run immediately&lt;/li&gt;
&lt;li&gt;Dominant palette swatches per character&lt;/li&gt;
&lt;li&gt;Prompt seed documents, if you want to generate variations in the same style&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why the Bible is the part I'd actually pitch
&lt;/h2&gt;

&lt;p&gt;Art you can find. What's usually missing is the reason behind it — why this character carries that weapon, how they relate to the rest of the roster, what their numbers should look like next to everyone else's.&lt;/p&gt;

&lt;p&gt;That's what the Bible covers, and it's why the four packs hold together as one cast rather than 40 unrelated images. If you're building a tactics game or an RPG with a roster, the stat cards and lore give you a starting point to argue with, which is much faster than starting from an empty document.&lt;/p&gt;

&lt;p&gt;You can also ignore all of it and just use the art. The documentation is there if you want it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who it's for
&lt;/h2&gt;

&lt;p&gt;Indie developers building RPGs, tactics games, visual novels, or anything with a named cast. It's PNG-based, so it drops into Unity, Unreal, Godot, GameMaker, Ren'Py, Twine, RPG Maker MV/MZ, Phaser, LÖVE, Foundry VTT, Roll20, Tabletop Simulator — anything that opens an image. The Ren'Py scripts are the only engine-specific thing in the bundle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Licence
&lt;/h2&gt;

&lt;p&gt;One purchase covers a solo dev or a full team, for any number of commercial and non-commercial projects. Modify and remix freely. No reselling the originals and no repackaging — full terms in &lt;code&gt;LICENCE.txt&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI disclosure
&lt;/h2&gt;

&lt;p&gt;These characters were designed and curated by me using AI-assisted illustration tools. Full commercial rights transfer on purchase. If you're reselling work that includes them on a storefront requiring AI disclosure, make that disclosure as their policy requires.&lt;/p&gt;

&lt;p&gt;I'm stating it here because the product page states it, and a post sending you to that page shouldn't be vaguer than the page itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get it
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://nasdigital.gumroad.com/l/CharacterPackBundle1" rel="noopener noreferrer"&gt;Ultimate Character Collection — All 4 Packs&lt;/a&gt;&lt;/strong&gt; — £24.99.&lt;/p&gt;

&lt;p&gt;The individual volumes are also available separately (£6.99–£12.99) if you only need one setting.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Made by NAS Digital. More game assets, design templates, and developer tools at &lt;a href="https://nasdigital.co.uk" rel="noopener noreferrer"&gt;nasdigital.co.uk&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>indiegame</category>
      <category>assets</category>
      <category>programming</category>
    </item>
    <item>
      <title>Semantic Kernel CVE-2026-25592: What You Need to Know</title>
      <dc:creator>With Nate</dc:creator>
      <pubDate>Tue, 09 Jun 2026 17:52:56 +0000</pubDate>
      <link>https://dev.to/nasdigital/semantic-kernel-cvss-100-vulnerability-what-you-need-to-know-1m77</link>
      <guid>https://dev.to/nasdigital/semantic-kernel-cvss-100-vulnerability-what-you-need-to-know-1m77</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Correction (July 2026):&lt;/strong&gt; an earlier version of this post gave the CVSS score as 10.0 and the disclosure date as 7 May 2026. Both were wrong — the score is &lt;strong&gt;9.9&lt;/strong&gt; and the advisory was published on &lt;strong&gt;6 February 2026&lt;/strong&gt;. That earlier version also described only &lt;code&gt;DownloadFileAsync&lt;/code&gt;; the advisory covers &lt;code&gt;UploadFileAsync&lt;/code&gt; as well, and the hardening advice below has been corrected accordingly. Apologies to anyone who acted on the original.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Microsoft disclosed CVE-2026-25592, an arbitrary-file-write vulnerability in Semantic Kernel — the .NET framework used to build AI agents and LLM-powered applications. The GitHub Security Advisory (&lt;a href="https://github.com/advisories/GHSA-2ww3-72rp-wpp4" rel="noopener noreferrer"&gt;GHSA-2ww3-72rp-wpp4&lt;/a&gt;) was published on 6 February 2026 with a CVSS 3.1 base score of &lt;strong&gt;9.9&lt;/strong&gt; (&lt;code&gt;AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;If you've upgraded to Semantic Kernel 1.71.0, you've applied Microsoft's official fix. Many teams considered the issue closed at that point.&lt;/p&gt;

&lt;p&gt;From a CVE perspective, they're correct.&lt;/p&gt;

&lt;p&gt;However, the vulnerability highlights a broader security problem that can still exist in many Semantic Kernel deployments: allowing AI-controlled values to flow into privileged operations without strict validation.&lt;/p&gt;

&lt;p&gt;This post explains what CVE-2026-25592 was, how it worked in a real .NET application, why the underlying pattern remains relevant, and what developers should be doing to secure their Semantic Kernel implementations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Semantic Kernel Matters
&lt;/h2&gt;

&lt;p&gt;Semantic Kernel is Microsoft's open-source orchestration framework for integrating Large Language Models into .NET applications. It provides abstractions for prompts, plugins, memory, planning, tool invocation, and agent workflows.&lt;/p&gt;

&lt;p&gt;It is increasingly being used to power:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enterprise customer support systems&lt;/li&gt;
&lt;li&gt;AI-assisted business workflow automation&lt;/li&gt;
&lt;li&gt;Internal knowledge management platforms&lt;/li&gt;
&lt;li&gt;Intelligent data processing pipelines&lt;/li&gt;
&lt;li&gt;Agentic applications connected to enterprise systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The framework works by exposing application functionality to the LLM through "kernel functions" — methods decorated with the &lt;code&gt;[KernelFunction]&lt;/code&gt; attribute that the model can invoke when it determines they are needed.&lt;/p&gt;

&lt;p&gt;This capability is what makes Semantic Kernel powerful.&lt;/p&gt;

&lt;p&gt;It is also what makes mistakes extremely dangerous.&lt;/p&gt;




&lt;h2&gt;
  
  
  CVE-2026-25592: The Sandbox That Wasn't
&lt;/h2&gt;

&lt;p&gt;The vulnerability centered around the &lt;code&gt;SessionsPythonPlugin&lt;/code&gt; component within Semantic Kernel's .NET SDK.&lt;/p&gt;

&lt;p&gt;The plugin exists to allow agents to execute Python code inside an Azure Container Apps sandbox. The intended security model is straightforward: code runs inside the isolated environment and cannot directly impact the host system.&lt;/p&gt;

&lt;p&gt;The problem was insufficient path validation in &lt;strong&gt;two&lt;/strong&gt; helper methods: &lt;code&gt;DownloadFileAsync&lt;/code&gt; and &lt;code&gt;UploadFileAsync&lt;/code&gt;. Both were exposed to the LLM through a &lt;code&gt;[KernelFunction]&lt;/code&gt; attribute, which turned internal helpers into AI-callable tools — and made their path parameters AI-controlled.&lt;/p&gt;

&lt;p&gt;Path traversal sequences (&lt;code&gt;../&lt;/code&gt;) or absolute paths could escape the intended directory and reach arbitrary locations writable by the application process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is the detail that matters most for remediation: fixing only &lt;code&gt;DownloadFileAsync&lt;/code&gt; leaves you exposed.&lt;/strong&gt; If you wrote your own mitigation based on early write-ups (including the first version of this post), check &lt;code&gt;UploadFileAsync&lt;/code&gt; too.&lt;/p&gt;

&lt;p&gt;An attacker who can influence any prompt consumed by the agent — a support ticket, uploaded document, SharePoint file, Teams message, RAG source, or direct user interaction — could potentially persuade the model to invoke either function with an attacker-chosen path.&lt;/p&gt;

&lt;p&gt;For example, a path pointing at the current user's startup folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="kd"&gt;C&lt;/span&gt;:\Users\&amp;lt;user&amp;gt;\AppData\Roaming\Microsoft\Windows\Start &lt;span class="kd"&gt;Menu&lt;/span&gt;\Programs\Startup\evil.exe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a malicious payload is written to a startup directory, the next user login may result in code execution. That escalation from "write a file" to "run code on the host" is why the score is 9.9 rather than something lower — the scope change (&lt;code&gt;S:C&lt;/code&gt;) reflects escaping the sandbox boundary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Affected and fixed versions:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SDK&lt;/th&gt;
&lt;th&gt;Vulnerable&lt;/th&gt;
&lt;th&gt;Fixed in&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;.NET (&lt;code&gt;Microsoft.SemanticKernel.Plugins.Core&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&amp;lt; 1.71.0&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.71.0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;&amp;lt; 1.39.3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.39.3&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The Broader Lesson
&lt;/h2&gt;

&lt;p&gt;Microsoft's fix addresses the specific vulnerability that was reported.&lt;/p&gt;

&lt;p&gt;However, the broader lesson extends beyond two methods.&lt;/p&gt;

&lt;p&gt;The root problem was allowing AI-generated values to reach privileged operations without appropriate validation.&lt;/p&gt;

&lt;p&gt;Although the reported vulnerability has been remediated, developers can unintentionally recreate the same class of issue in custom implementations.&lt;/p&gt;

&lt;p&gt;Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custom kernel functions that accept file paths&lt;/li&gt;
&lt;li&gt;Functions that construct URLs&lt;/li&gt;
&lt;li&gt;Database query generation&lt;/li&gt;
&lt;li&gt;Command execution wrappers&lt;/li&gt;
&lt;li&gt;Reflection-based operations&lt;/li&gt;
&lt;li&gt;Network-access plugins&lt;/li&gt;
&lt;li&gt;Internal API integrations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a custom &lt;code&gt;[KernelFunction]&lt;/code&gt; accepts AI-controlled input and passes it directly into operating system, database, filesystem, or network operations, the same trust-boundary problem can reappear regardless of Semantic Kernel version.&lt;/p&gt;

&lt;p&gt;The important question is not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Am I running 1.71.0?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The important question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Do any of my kernel functions trust values generated by an LLM?"&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Hardened Implementation Pattern
&lt;/h2&gt;

&lt;p&gt;The safest approach is to avoid automatic execution of sensitive functions wherever possible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;executionSettings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;OpenAIPromptExecutionSettings&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ToolCallBehavior&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ToolCallBehavior&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EnableKernelFunctions&lt;/span&gt;
    &lt;span class="c1"&gt;// Avoid AutoInvokeKernelFunctions for privileged operations&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When functions must accept paths, validate against an allowlist rather than trying to block dangerous values. Note this pattern applies to &lt;strong&gt;any&lt;/strong&gt; path-taking kernel function — upload as well as download.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;ResolveWithinAllowedRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;requested&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;allowedRoot&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFullPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/app/downloads"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TrimEnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DirectorySeparatorChar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DirectorySeparatorChar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// GetFileName strips any directory component, so "../../etc/passwd"&lt;/span&gt;
    &lt;span class="c1"&gt;// collapses to "passwd" before it is ever combined with the root.&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;resolved&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFullPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allowedRoot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFileName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requested&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;resolved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allowedRoot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ordinal&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SecurityException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Path traversal attempt: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;requested&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;resolved&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;KernelFunction&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;DownloadFileAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;localFilePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;safePath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ResolveWithinAllowedRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;localFilePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;safePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// ... write the downloaded content to stream ...&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;safePath&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A note on the comparison: use &lt;code&gt;StringComparison.Ordinal&lt;/code&gt; rather than &lt;code&gt;OrdinalIgnoreCase&lt;/code&gt; unless you are certain the filesystem is case-insensitive. Getting this wrong on Linux containers can accept paths you meant to reject.&lt;/p&gt;

&lt;p&gt;The key principle is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Never trust AI-generated input simply because it originated from your own application.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Audit Every AI-Initiated Action
&lt;/h2&gt;

&lt;p&gt;One of the most effective defensive controls is auditing all AI-triggered function calls.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;kernel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FunctionInvocationFilters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SecurityAuditFilter&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A security-focused invocation filter should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Log every tool invocation&lt;/li&gt;
&lt;li&gt;Capture relevant arguments&lt;/li&gt;
&lt;li&gt;Alert on filesystem access&lt;/li&gt;
&lt;li&gt;Alert on network operations&lt;/li&gt;
&lt;li&gt;Alert on database modifications&lt;/li&gt;
&lt;li&gt;Generate audit events for investigation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you cannot see what your agent is doing, you cannot detect when it has been manipulated.&lt;/p&gt;




&lt;h2&gt;
  
  
  Human Approval Controls
&lt;/h2&gt;

&lt;p&gt;Many enterprise teams rely on human-in-the-loop controls before allowing agents to perform sensitive actions.&lt;/p&gt;

&lt;p&gt;Semantic Kernel includes mechanisms intended to support these workflows. However, developers have reported scenarios where confirmation behaviour did not align with their expectations.&lt;/p&gt;

&lt;p&gt;For that reason, organizations with strict approval requirements should consider implementing confirmation logic explicitly at the application layer rather than relying solely on framework-level controls.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConfirmationRequiredFilter&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IFunctionInvocationFilter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IConfirmationService&lt;/span&gt; &lt;span class="n"&gt;_confirmationService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ConfirmationRequiredFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IConfirmationService&lt;/span&gt; &lt;span class="n"&gt;confirmationService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_confirmationService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;confirmationService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;OnFunctionInvocationAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;FunctionInvocationContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FunctionInvocationContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;approved&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_confirmationService&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RequestApprovalAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Arguments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;approved&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OperationCanceledException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Human approval denied."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Immediate Actions for Production Deployments
&lt;/h2&gt;

&lt;p&gt;If you're currently running Semantic Kernel in production:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Upgrade
&lt;/h3&gt;

&lt;p&gt;.NET: 1.71.0 or later. Python: 1.39.3 or later.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Audit Every Kernel Function
&lt;/h3&gt;

&lt;p&gt;Review every &lt;code&gt;[KernelFunction]&lt;/code&gt; implementation and identify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Filesystem operations (both read &lt;em&gt;and&lt;/em&gt; write paths)&lt;/li&gt;
&lt;li&gt;Network requests&lt;/li&gt;
&lt;li&gt;Database access&lt;/li&gt;
&lt;li&gt;Process execution&lt;/li&gt;
&lt;li&gt;Reflection usage&lt;/li&gt;
&lt;li&gt;Dynamic code generation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Treat all parameters as untrusted input.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Disable AutoInvoke for Sensitive Operations
&lt;/h3&gt;

&lt;p&gt;Only allow automatic invocation for low-risk, read-only functions. Require explicit approval for anything that modifies systems or data.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Implement Allowlist Validation
&lt;/h3&gt;

&lt;p&gt;Validate paths, URLs, queries, commands, and resource identifiers. Do not rely on blocklists.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Run With Least Privilege
&lt;/h3&gt;

&lt;p&gt;Minimal filesystem permissions, restricted network access, limited IAM permissions, regular credential rotation, strong isolation where practical.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Monitor for Exploitation Attempts
&lt;/h3&gt;

&lt;p&gt;Look for unexpected tool invocations, unusual filesystem activity, unexpected outbound traffic, and repeated failed function calls followed by success.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Review Historical Activity
&lt;/h3&gt;

&lt;p&gt;If vulnerable versions were previously deployed: review audit logs, investigate unusual agent behaviour, check for unauthorized file modifications, and rotate secrets where appropriate.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bigger Picture
&lt;/h2&gt;

&lt;p&gt;CVE-2026-25592 highlights a challenge facing every AI framework today.&lt;/p&gt;

&lt;p&gt;Developers want agents that can take meaningful action. Security teams want systems that cannot be manipulated. Those goals are often in tension.&lt;/p&gt;

&lt;p&gt;Semantic Kernel is not unique here. Similar patterns can emerge in any framework where an LLM is allowed to invoke application functionality. Whenever model-generated output crosses into privileged operations, the trust boundary becomes a security-critical control.&lt;/p&gt;

&lt;p&gt;The industry is still learning how to secure these systems properly.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'm Looking At Next
&lt;/h2&gt;

&lt;p&gt;These issues are only part of a much larger attack surface. Future research will focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Azure AI Search index poisoning through SharePoint and Teams content&lt;/li&gt;
&lt;li&gt;Text-to-SQL injection chains in Azure OpenAI applications&lt;/li&gt;
&lt;li&gt;Secure patterns for agentic workflows&lt;/li&gt;
&lt;li&gt;Building Roslyn analyzers that detect dangerous Semantic Kernel patterns at compile time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI agents are becoming increasingly capable. The security controls protecting them need to mature just as quickly.&lt;/p&gt;

&lt;p&gt;Until then, treat every AI-generated action as untrusted input and design your systems accordingly.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Sources:&lt;/strong&gt; &lt;a href="https://github.com/advisories/GHSA-2ww3-72rp-wpp4" rel="noopener noreferrer"&gt;GitHub Security Advisory GHSA-2ww3-72rp-wpp4&lt;/a&gt; · &lt;a href="https://advisories.gitlab.com/pkg/nuget/microsoft.semantickernel.core/CVE-2026-25592/" rel="noopener noreferrer"&gt;GitLab Advisory Database&lt;/a&gt;&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Working with Semantic Kernel in production? The &lt;a href="https://nasdigital.gumroad.com/l/semantic-kernel-security-audit" rel="noopener noreferrer"&gt;Semantic Kernel Security Audit Prompt Pack&lt;/a&gt; walks through the attack surfaces covered in this series, with 20 ready-to-use audit prompts for the vulnerability classes Microsoft hasn't fully addressed.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>aisecurity</category>
      <category>semantickernel</category>
      <category>dotnet</category>
    </item>
  </channel>
</rss>
