<?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: Liam Romanis</title>
    <description>The latest articles on DEV Community by Liam Romanis (@lromanis).</description>
    <link>https://dev.to/lromanis</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3936947%2Fa3f407c9-33fe-4d0b-805e-2921952f8e89.png</url>
      <title>DEV Community: Liam Romanis</title>
      <link>https://dev.to/lromanis</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lromanis"/>
    <language>en</language>
    <item>
      <title>Microsoft's Costly Mistakes, and What Europe Is Doing About Them</title>
      <dc:creator>Liam Romanis</dc:creator>
      <pubDate>Wed, 03 Jun 2026 11:43:31 +0000</pubDate>
      <link>https://dev.to/lromanis/microsofts-costly-mistakes-and-what-europe-is-doing-about-them-j0g</link>
      <guid>https://dev.to/lromanis/microsofts-costly-mistakes-and-what-europe-is-doing-about-them-j0g</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkskz5ycgxb84pjwvj0dy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkskz5ycgxb84pjwvj0dy.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's a particular kind of corporate confusion that happens when a company gets so fixated on where it wants to go that it stops paying attention to what it's destroying on the way there. Microsoft, a company that has navigated more technological transitions than almost any other in the industry, is displaying that confusion at scale right now, and the consequences are starting to land in places that will be very difficult to walk back.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Recall Disaster That Refuses to End
&lt;/h2&gt;

&lt;p&gt;Start with Windows Recall, because it tells you almost everything you need to know about Microsoft's current decision-making culture.&lt;/p&gt;

&lt;p&gt;First announced in 2024, Recall was positioned as a flagship AI feature for Windows 11, a tool that would take a screenshot of your desktop every few seconds and build a searchable, AI-indexed timeline of everything you'd ever done on your computer. The privacy implications were immediately obvious to virtually everyone except, apparently, the people who approved it for launch. The original version stored all of that screenshot data unencrypted, in plaintext: passwords, financial information, private communications, all of it sitting in a folder, waiting. Security researchers had a field day. Microsoft pulled the feature before it ever shipped.&lt;/p&gt;

&lt;p&gt;What followed was a year-long rework. Encryption was added. Biometric authentication was required to access the data. Microsoft declared the problem solved and shipped it again in April 2025. Independent testing in August 2025 found the sensitive information filter still failing to catch credit card numbers, bank balances, Social Security numbers, and passwords. As of early 2026, Microsoft is reportedly pulling back its entire Windows 11 AI push and rethinking Recall from the ground up.&lt;/p&gt;

&lt;p&gt;The technology problem here is real but fixable. The cultural problem is harder: how does a company with Microsoft's resources and security expertise ship a feature that stores your passwords in plaintext as a flagship product? The answer seems to be that the pressure to demonstrate AI momentum overwhelmed the engineering discipline that should have caught it. That's a worrying pattern at a company with Windows installed on over a billion devices.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Gaming Empire, Methodically Dismantled
&lt;/h2&gt;

&lt;p&gt;The Xbox story is a different kind of confusion, but the same underlying dynamic.&lt;/p&gt;

&lt;p&gt;Microsoft spent $69 billion acquiring Activision Blizzard, the largest acquisition in gaming history, positioning itself as a dominant force in interactive entertainment. The argument was scale: own the content, own the platform, own the subscription. Then, having made that argument convincingly enough to clear regulators on both sides of the Atlantic, Microsoft began quietly dismantling the creative studios that were supposed to make it meaningful. The Initiative, the studio behind the rebooted Perfect Dark, was shuttered. Everwild, years in development at Rare, was cancelled. The pattern was consistent enough to suggest a strategy, not a series of isolated decisions.&lt;/p&gt;

&lt;p&gt;The gaming division is being, in the words of one analyst, "harvested", its resources redirected toward AI infrastructure. That may make sense on a spreadsheet. It makes considerably less sense as a stewardship of a $69 billion creative asset, or as a proposition to the developers and studios who were recruited under a very different set of promises.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fifteen Thousand Jobs, Record Revenue
&lt;/h2&gt;

&lt;p&gt;In May and July of 2025, Microsoft cut roughly 15,000 positions across engineering, marketing, and HR. The explicit rationale from CEO Satya Nadella was that AI tools, including Copilot, were now writing around 30% of the company's code, reducing the need for human engineers. This is a reasonable thing to say if you're trying to justify to Wall Street why your headcount is falling.&lt;/p&gt;

&lt;p&gt;It is a harder thing to say when, in the same quarter, you report $70 billion in revenue and continued strong cloud growth. These were not austerity cuts. They were, by Microsoft's own framing, a reallocation: human capital out, AI infrastructure in. The message to the remaining workforce, and to anyone considering joining, is clear enough. The longer-term effect on institutional knowledge, on culture, and on the decade of goodwill that Nadella built when he arrived and humanised a company that had grown rigid and territorial, is harder to quantify but unlikely to be positive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skype: A $8.5 Billion Lesson in Benign Neglect
&lt;/h2&gt;

&lt;p&gt;Skype shut down in May 2025, finally and definitively, with its users redirected to the free version of Microsoft Teams. This was not a surprise. Skype had been functionally abandoned for years, its daily active users collapsing from 300 million at peak to around 36 million by the time Microsoft put it out of its misery. The surprise, looked at from any distance, is how thoroughly Microsoft managed to destroy something it paid $8.5 billion for in 2011.&lt;/p&gt;

&lt;p&gt;Skype didn't lose to Zoom on technology. It lost because Microsoft redesigned it in ways users actively hated, starved it of investment, and failed to respond to competitors for long enough that the market simply moved on. It's an instructive case study in how incumbent advantage erodes: not through a single catastrophic failure, but through years of quiet disinterest.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub's Silent Bans and the Problem of Invisible Power
&lt;/h2&gt;

&lt;p&gt;Microsoft's ownership of GitHub rarely surfaces in everyday conversation, but it matters. GitHub is not simply a code-hosting service. For most of the world's software developers, it is the infrastructure of their professional lives: their portfolio, their collaborative workspace, their contribution history. Over 100 million developers depend on it. When Microsoft makes decisions about how GitHub operates, those decisions land with the force of a utility provider, not a website.&lt;/p&gt;

&lt;p&gt;Which makes the events of late 2025 worth examining carefully. In October of that year, GitHub quietly updated its acceptable use policies to prohibit content that is sexually themed or suggestive with no clear creative or educational purpose. The policy change itself was not unreasonable on its face. What followed, however, was a wave of silent, unexplained account suspensions that affected an estimated 80 to 90 repositories and 40 to 50 developers, the majority of them members of modding and plugin communities for adult games. In most cases, GitHub did not inform users which specific terms they had violated. Accounts simply returned a 404 error. Years of collaborative work, across dozens of contributors in some cases, vanished without notice or meaningful right of appeal.&lt;/p&gt;

&lt;p&gt;The pattern is not new. Developers in regions subject to US sanctions have reported similar experiences for several years, finding their accounts restricted or suspended with no warning and facing an appeals process that, in documented cases, has gone unanswered for months. One developer cited being stuck since March 2025 with no response after multiple appeals, despite providing evidence they were not located in a sanctioned area.&lt;/p&gt;

&lt;p&gt;The consistent thread across these cases is not that GitHub enforces its terms of service, which it is entitled to do, but that it does so with a near-total absence of transparency, explanation, or recourse. For a platform that positions itself as the home for all developers, that gap between aspiration and practice is significant.&lt;/p&gt;

&lt;p&gt;There is also a broader structural concern. When a single company controls the dominant repository platform for the world's open source development, content moderation decisions taken in Redmond or San Francisco have global consequences. Developers whose livelihoods or projects are disrupted have nowhere equivalent to go, which is precisely why the manner in which that power is exercised deserves far more scrutiny than it currently receives. Driving communities underground, onto fragmented or less visible platforms, does not make the content disappear. It simply removes the visibility and accountability that a mainstream platform, however imperfectly, provides.&lt;/p&gt;

&lt;h2&gt;
  
  
  Europe's Response May Be Exactly What the Market Needs
&lt;/h2&gt;

&lt;p&gt;Here is where Microsoft's internal difficulties connect to something larger, and arguably something healthier.&lt;/p&gt;

&lt;p&gt;The European Union is, right now, in the middle of a significant and accelerating effort to reduce its dependence on American technology infrastructure. In October 2025, the European Commission launched a sovereign cloud procurement framework, and in April 2026 it awarded a €180 million contract for cloud services to four European providers, explicitly bypassing Amazon, Google, and Microsoft, which together control 63% of the global cloud market. A decisive vote on formalising those procurement rules is scheduled for June 2026. Denmark has begun transitioning government ministries from Microsoft Office 365 to LibreOffice. Several other member states have followed or are actively considering similar moves to Linux-based alternatives. The European Central Bank selected a European provider for its digital euro infrastructure. The Dutch parliament passed eight motions urging reduced dependence on US technology. The mood across the continent is one of deliberate, structural disengagement.&lt;/p&gt;

&lt;p&gt;The political catalyst is partly Donald Trump. Specifically, the concern among European governments that American technology companies could, under sufficient geopolitical pressure, restrict or withdraw services to European users. An ICC judge losing access to his Visa card after US sanctions is a small thing; the same logic applied to cloud infrastructure is not. European officials point to it regularly when explaining why this shift feels urgent rather than merely desirable.&lt;/p&gt;

&lt;p&gt;But whatever the political trigger, the competitive consequences are worth taking seriously in their own right. Markets work best when dominance is contested. For the better part of two decades, Microsoft, Google, and Amazon have divided the enterprise technology landscape between them with relatively little pressure from credible alternatives. The result, as the decisions catalogued above suggest, is a culture in which a company can ship a feature storing your passwords in plaintext, cut fifteen thousand jobs during a record revenue quarter, and abandon a $8.5 billion acquisition through sheer neglect, with no meaningful market consequence.&lt;/p&gt;

&lt;p&gt;European governments moving to Linux desktops and sovereign cloud infrastructure changes that calculation. It signals to the market that the incumbents are not, in fact, irreplaceable. It creates space for alternative ecosystems to mature and for genuine competition to develop. It may also, in time, force Microsoft and its peers to remember that trust is a product feature, not a marketing position.&lt;/p&gt;

&lt;p&gt;None of this will be painless. European cloud alternatives are improving but are not yet equivalent at enterprise scale, and governments switching to Linux face real transition costs and capability gaps. The road from dependency to sovereignty is long and expensive. But the destination, a technology market where no single vendor can afford to take its users for granted, is a genuinely better one. If Microsoft's recent run of poor decisions has helped accelerate that journey, it may turn out to be the most consequential contribution the company makes in the 2020s, though not in the way Satya Nadella intended.&lt;/p&gt;

</description>
      <category>microsoft</category>
      <category>github</category>
      <category>eu</category>
    </item>
    <item>
      <title>CIFSwitch - CVE-2026-46243</title>
      <dc:creator>Liam Romanis</dc:creator>
      <pubDate>Wed, 03 Jun 2026 00:33:11 +0000</pubDate>
      <link>https://dev.to/lromanis/cifswitch-cve-2026-46243-1d9e</link>
      <guid>https://dev.to/lromanis/cifswitch-cve-2026-46243-1d9e</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7qr6651t8kwzplp2g61s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7qr6651t8kwzplp2g61s.png" alt=" " width="800" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just released an open-source bash checker for CIFSwitch (CVE-2026-46243) — the 19-year-old Linux kernel LPE disclosed last week that lets any unprivileged local user get root by abusing the CIFS/SPNEGO upcall path.&lt;/p&gt;

&lt;p&gt;The script runs on bare-metal, VMs, and inside containers, and is CI/CD-friendly with JSON output and clean exit codes.&lt;/p&gt;

&lt;p&gt;It checks:&lt;br&gt;
✅ Kernel version against patched thresholds (6.18.22 / 6.19.12 / 7.0+)&lt;br&gt;
✅ cifs-utils presence and exploitable version&lt;br&gt;
✅ CIFS kernel module load state and blacklist status&lt;br&gt;
✅ Unprivileged user namespace sysctl (the pivot point for the exploit)&lt;br&gt;
✅ Active request-key cifs.spnego rules&lt;br&gt;
✅ SELinux / AppArmor enforcement&lt;br&gt;
✅ Container capabilities (CAP_SYS_ADMIN)&lt;br&gt;
✅ Kernel symbol verification for the fix commit&lt;/p&gt;

&lt;p&gt;Outputs human-readable or JSON for SIEM ingestion. Exit 0 = safe, exit 1 = action needed — drop it straight into a pipeline.&lt;/p&gt;

&lt;p&gt;CIFSwitch is the fourth Linux LPE in under six weeks (after Copy Fail, Dirty Frag, and Fragnesia). If you're running multi-tenant Linux, CI runners, or container build farms, now is a good time to audit.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/liamromanis101" rel="noopener noreferrer"&gt;
        liamromanis101
      &lt;/a&gt; / &lt;a href="https://github.com/liamromanis101/cifswitch-check" rel="noopener noreferrer"&gt;
        cifswitch-check
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Detection script for CIFSwitch - CVE-2026-46243
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;cifswitch-check&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A shell script to check whether a Linux system is exposed to &lt;strong&gt;CIFSwitch (CVE-2026-46243)&lt;/strong&gt; — a local privilege escalation vulnerability in the Linux kernel's CIFS/SMB client that has been present since 2007.&lt;/p&gt;
&lt;p&gt;Runs on bare-metal hosts, VMs, and inside containers. Designed to drop straight into CI/CD pipelines.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Background&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;CIFSwitch was disclosed on 28 May 2026 by security researcher &lt;a href="https://heyitsas.im/posts/cifswitch/" rel="nofollow noopener noreferrer"&gt;Asim Manizada&lt;/a&gt;. The flaw chains a missing input validation in the kernel's &lt;code&gt;cifs.spnego&lt;/code&gt; key type with the rootful &lt;code&gt;cifs.upcall&lt;/code&gt; helper from &lt;code&gt;cifs-utils&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;An unprivileged local user can call &lt;code&gt;request_key()&lt;/code&gt; with a forged key description, causing the kernel to invoke &lt;code&gt;cifs.upcall&lt;/code&gt; as root with attacker-controlled fields. With &lt;code&gt;upcall_target=app&lt;/code&gt;, the helper enters the attacker's mount namespace and performs a &lt;code&gt;getpwuid()&lt;/code&gt; lookup before dropping privileges — loading an attacker-controlled NSS module and executing arbitrary code as root.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites for exploitation:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A vulnerable kernel (present since 2007, fixed in 6.18.22 / 6.19.12…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/liamromanis101/cifswitch-check" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;I have also updated the cve_checks.conf in my my K8s-container_escape_audit toolkit to detect this issue. &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/liamromanis101" rel="noopener noreferrer"&gt;
        liamromanis101
      &lt;/a&gt; / &lt;a href="https://github.com/liamromanis101/K8s-container_escape_audit" rel="noopener noreferrer"&gt;
        K8s-container_escape_audit
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Look for possible escape vectors from a container
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;K8s_container-escape-audit&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A bash script that runs inside a Docker or Kubernetes container and checks for escape vectors. Built for penetration testers and security teams doing container security assessments.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;For authorised security assessments only. Do not run this on systems you don't have explicit written permission to test.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it does&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;container_escape_audit.sh&lt;/code&gt; v4.0 performs &lt;strong&gt;47 checks&lt;/strong&gt; plus a config-driven CVE engine, covering: privileged configuration, dangerous capabilities, namespace isolation, filesystem mounts, kernel exposure, Kubernetes misconfigurations, cloud metadata access, kernel hardening posture, and an updateable database of recent kernel CVEs. All checks are strictly read-only — the script makes no changes to the system.&lt;/p&gt;
&lt;p&gt;Each finding comes with a structured report entry:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it is&lt;/strong&gt;: the misconfiguration or exposure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Impact&lt;/strong&gt;: worst-case if exploited&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exploitability&lt;/strong&gt;: difficulty, tooling, real-world precedent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recommendation&lt;/strong&gt;: specific remediation steps&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The tool ships as two files that must sit in the same directory:&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;
&lt;pre class="notranslate"&gt;&lt;code&gt;container_escape_audit.sh   # main script&lt;/code&gt;&lt;/pre&gt;…&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/liamromanis101/K8s-container_escape_audit" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>linux</category>
      <category>containers</category>
      <category>security</category>
      <category>devops</category>
    </item>
    <item>
      <title>I built a container escape audit tool — here's what v4.0 adds</title>
      <dc:creator>Liam Romanis</dc:creator>
      <pubDate>Mon, 01 Jun 2026 13:24:47 +0000</pubDate>
      <link>https://dev.to/lromanis/i-built-a-container-escape-audit-tool-heres-what-v40-adds-4m8</link>
      <guid>https://dev.to/lromanis/i-built-a-container-escape-audit-tool-heres-what-v40-adds-4m8</guid>
      <description>&lt;h2&gt;
  
  
  canonical_url: &lt;a href="https://github.com/liamromanis101/K8s-container_escape_audit" rel="noopener noreferrer"&gt;https://github.com/liamromanis101/K8s-container_escape_audit&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Container security tooling tends to fall into two camps: heavyweight scanners that run outside the container before deployment, and ad-hoc one-liners you paste into a shell when something looks wrong. &lt;code&gt;container_escape_audit.sh&lt;/code&gt; sits in neither — it runs &lt;em&gt;inside&lt;/em&gt; a live container, checks the actual runtime environment, and tells you exactly what an attacker who just landed in that container would be looking at.&lt;/p&gt;

&lt;p&gt;Version 4.0 adds 12 kernel hardening checks, a config-driven CVE engine, and a database of 10 current kernel CVEs — including three that are actively exploited in the wild right now. This post walks through what's new and why the config-driven approach matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/liamromanis101/K8s-container_escape_audit" rel="noopener noreferrer"&gt;github.com/liamromanis101/K8s-container_escape_audit&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The quick version
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# grab both files&lt;/span&gt;
curl &lt;span class="nt"&gt;-sO&lt;/span&gt; https://raw.githubusercontent.com/liamromanis101/K8s-container_escape_audit/main/container_escape_audit.sh
curl &lt;span class="nt"&gt;-sO&lt;/span&gt; https://raw.githubusercontent.com/liamromanis101/K8s-container_escape_audit/main/cve_checks.conf
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x container_escape_audit.sh
./container_escape_audit.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It runs in 15–45 seconds, writes a structured report, and exits. No installation. No root required (though running as root inside the container gives you more complete results). Nothing is written to the system — every check is read-only.&lt;/p&gt;

&lt;p&gt;Output looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[CRIT]  Container appears PRIVILEGED (CapEff=0000003fffffffff)
[CRIT]  VULNERABLE to Copy Fail (CVE-2026-31431) — AEAD socket bindable, kernel 6.1.112
[WARN]  kptr_restrict=1 — kernel pointers visible to root processes
[WARN]  Unprivileged user namespaces enabled (kernel.unprivileged_userns_clone=1)
[ OK ]  cgroup v2 subtree_control is not writable
[ OK ]  No readable SSH private keys found

==================== SUMMARY ====================
  [CRITICAL] Container is running in privileged mode
  [CRITICAL] Copy Fail (CVE-2026-31431) AF_ALG exposure — CRITICAL [ITW] [CISA-KEV]
  [HIGH    ] kptr_restrict=1: kernel pointers visible to root
  [HIGH    ] Unprivileged user namespace creation is enabled

  CRITICAL: 2  |  HIGH: 5  |  MEDIUM: 3  |  INFO: 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every finding in the full report has four fields: what it is, impact, exploitability, and recommendation.&lt;/p&gt;




&lt;h2&gt;
  
  
  What it checks
&lt;/h2&gt;

&lt;p&gt;The script runs 47 checks across four sections plus the CVE engine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checks 1–23&lt;/strong&gt; are the classic container escape vectors most people are familiar with — privileged mode, dangerous capabilities, host namespace sharing, dangerous mounts, &lt;code&gt;/proc&lt;/code&gt; exposure, Kubernetes service account tokens, writable cron and auth files, runtime sockets, SUID binaries, and so on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checks 24–35&lt;/strong&gt; cover newer runtime attack surface: NVIDIAScape (CVE-2025-23266), the runc masked-path race trio (CVE-2025-31133/-52565/-52881), eBPF exposure, debugfs, Kubernetes RBAC active probing via &lt;code&gt;SelfSubjectAccessReview&lt;/code&gt;, kernel keyring access, OCI hook injection paths, page cache write primitives (splice + pipe2), and procfs namespace FD leakage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checks 36–47&lt;/strong&gt; are new in v4.0 — kernel hardening posture. More on these below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The CVE engine&lt;/strong&gt; reads &lt;code&gt;cve_checks.conf&lt;/code&gt; and runs compound checks against each entry. Also new in v4.0.&lt;/p&gt;




&lt;h2&gt;
  
  
  New: kernel hardening checks
&lt;/h2&gt;

&lt;p&gt;When you're auditing a container, you're looking at the host kernel's sysctl values too — they reflect directly what mitigations are and aren't active. All of these are read from &lt;code&gt;/proc/sys&lt;/code&gt; with no writes, no side effects.&lt;/p&gt;

&lt;p&gt;The ones that tend to be most impactful in practice:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;kernel.kptr_restrict&lt;/code&gt;&lt;/strong&gt; — if this is 0, every user on the box can read kernel symbol addresses from &lt;code&gt;/proc/kallsyms&lt;/code&gt;. That's an instant KASLR bypass. Most exploits against kernel vulnerabilities need an address leak as step one; when &lt;code&gt;kptr_restrict=0&lt;/code&gt; you skip that step entirely. It should be 2.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;kernel.unprivileged_userns_clone&lt;/code&gt;&lt;/strong&gt; — unprivileged user namespaces are the prerequisite for the majority of container escape CVEs published since 2019. Flipping Pages (CVE-2024-1086), the Packet Socket Race (CVE-2025-38617), Copy Fail, Dirty Frag — all of them either require user namespaces or become significantly easier with them. Setting this to 0 on hosts that don't need rootless containers removes a huge amount of attack surface in one sysctl.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;kernel.perf_event_paranoid&lt;/code&gt;&lt;/strong&gt; — at value 0 or -1, unprivileged processes can access kernel-level performance counters. This is the foundation of Spectre-class side-channel attacks and enables cross-container information leakage on shared CPU nodes. It should be at least 2.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;fs.protected_symlinks&lt;/code&gt; and &lt;code&gt;fs.protected_hardlinks&lt;/code&gt;&lt;/strong&gt; — classic /tmp race conditions. Still come up regularly in privilege escalation chains. Both should be 1.&lt;/p&gt;

&lt;p&gt;The full list in v4.0:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Recommended&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;36&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kernel.kptr_restrict&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;37&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kernel.dmesg_restrict&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;38&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kernel.randomize_va_space&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;39&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;fs.protected_symlinks&lt;/code&gt; / &lt;code&gt;fs.protected_hardlinks&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;fs.protected_fifos&lt;/code&gt; / &lt;code&gt;fs.protected_regular&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;41&lt;/td&gt;
&lt;td&gt;&lt;code&gt;net.ipv4.tcp_syncookies&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;td&gt;ICMP redirects / source routing / rp_filter&lt;/td&gt;
&lt;td&gt;0 / 0 / 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;43&lt;/td&gt;
&lt;td&gt;IP forwarding&lt;/td&gt;
&lt;td&gt;informational&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;44&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kernel.unprivileged_userns_clone&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;45&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kernel.perf_event_paranoid&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;≥ 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;46&lt;/td&gt;
&lt;td&gt;Dirty Frag modules (esp4, esp6, rxrpc)&lt;/td&gt;
&lt;td&gt;not loaded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;47&lt;/td&gt;
&lt;td&gt;Dangerous loaded modules audit&lt;/td&gt;
&lt;td&gt;14 modules&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  New: config-driven CVE checks
&lt;/h2&gt;

&lt;p&gt;Previously, CVE-specific checks were hardcoded functions in the script. Adding a new one meant modifying the script. That's fine for a handful of checks, but it doesn't scale well — and it means the script and the CVE data are tightly coupled when they really shouldn't be.&lt;/p&gt;

&lt;p&gt;In v4.0, CVE checks are defined in &lt;code&gt;cve_checks.conf&lt;/code&gt;. The script reads the file at runtime and dispatches the right test for each entry. To add a new CVE, you append a block to the config. The script doesn't change.&lt;/p&gt;

&lt;p&gt;A config entry looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="py"&gt;cve_id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;CVE-2024-1086&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Flipping Pages&lt;/span&gt;
&lt;span class="py"&gt;cvss&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;7.8&lt;/span&gt;
&lt;span class="py"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;CRITICAL&lt;/span&gt;
&lt;span class="py"&gt;check_type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;compound&lt;/span&gt;
&lt;span class="py"&gt;introduced&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;3.15&lt;/span&gt;
&lt;span class="py"&gt;fixed_versions&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;5.15:5.15.149 6.1:6.1.76 6.6:6.6.15&lt;/span&gt;
&lt;span class="py"&gt;itw&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;
&lt;span class="py"&gt;poc_public&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;
&lt;span class="py"&gt;cisa_kev&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;
&lt;span class="py"&gt;subsystem&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;net/netfilter/nf_tables&lt;/span&gt;
&lt;span class="py"&gt;module_names&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;nf_tables&lt;/span&gt;
&lt;span class="py"&gt;mitigation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;rmmod nf_tables 2&amp;gt;/dev/null; echo 'install nf_tables /bin/false' &amp;gt; /etc/modprobe.d/nftables.conf&lt;/span&gt;
&lt;span class="py"&gt;socket_af&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;
&lt;span class="py"&gt;socket_type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;
&lt;span class="py"&gt;socket_proto&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;
&lt;span class="py"&gt;what&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;CVE-2024-1086 is a use-after-free in nf_tables...&lt;/span&gt;
&lt;span class="py"&gt;impact&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Full local privilege escalation...&lt;/span&gt;
&lt;span class="py"&gt;exploit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Public PoC, 99.4% success rate on Debian/Ubuntu/KernelCTF...&lt;/span&gt;
&lt;span class="py"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Patch to v5.15.149+, v6.1.76+, or v6.6.15+...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;check_type&lt;/code&gt; field drives what the engine actually does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;kernel_version&lt;/code&gt;&lt;/strong&gt; — parses &lt;code&gt;uname -r&lt;/code&gt; and compares against the &lt;code&gt;introduced&lt;/code&gt;/&lt;code&gt;fixed_versions&lt;/code&gt; ranges. Handles all current LTS series.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;module_loaded&lt;/code&gt;&lt;/strong&gt; — checks &lt;code&gt;/proc/modules&lt;/code&gt; for the listed modules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;socket_family&lt;/code&gt;&lt;/strong&gt; — tries to open a socket with the given AF/type/proto from Python, which tells you whether the attack surface is reachable from within this specific container regardless of kernel patch status.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;compound&lt;/code&gt;&lt;/strong&gt; — runs all three and synthesises a combined severity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The compound severity logic is worth spelling out because it avoids the two failure modes of noisy and silent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kernel in affected range + module loaded or socket reachable  →  CRITICAL
kernel in affected range + module not blacklisted             →  HIGH (auto-load risk)
kernel in affected range + module blacklisted                 →  MEDIUM (interim mitigation, patch needed)
kernel not in affected range                                  →  INFO
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last HIGH case catches the thing that trips people up: a module that's not currently loaded but also not blacklisted can be auto-loaded by the kernel just by opening the right socket. "Not loaded" is not the same as "not exploitable."&lt;/p&gt;




&lt;h2&gt;
  
  
  What's in the CVE database
&lt;/h2&gt;

&lt;p&gt;The shipped &lt;code&gt;cve_checks.conf&lt;/code&gt; has ten entries. Three are actively exploited right now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Copy Fail (CVE-2026-31431, CVSS 7.8, CISA KEV)&lt;/strong&gt; — a flaw in the &lt;code&gt;algif_aead&lt;/code&gt; AF_ALG interface that gives any unprivileged user a 4-byte write into the page cache of any readable executable. A 732-byte Python PoC with no dependencies achieves reliable root. Affects kernels from 4.14. Interim mitigation: &lt;code&gt;rmmod algif_aead &amp;amp;&amp;amp; echo 'install algif_aead /bin/false' &amp;gt; /etc/modprobe.d/copyfail.conf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dirty Frag (CVE-2026-43284 + CVE-2026-43500, CVSS 8.8/7.8)&lt;/strong&gt; — same bug class as Copy Fail but through the IPsec ESP and RxRPC subsystems. Provides full attacker-controlled page cache writes at any offset, not just 4 bytes. Two CVEs, typically chained. As of writing, no distro patch exists for the RxRPC path — blacklisting &lt;code&gt;rxrpc&lt;/code&gt; is the only mitigation. Note: blacklisting &lt;code&gt;esp4&lt;/code&gt;/&lt;code&gt;esp6&lt;/code&gt; kills IPsec — check before you fleet-deploy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flipping Pages (CVE-2024-1086, CVSS 7.8, CISA KEV)&lt;/strong&gt; — use-after-free in &lt;code&gt;nf_tables&lt;/code&gt;. 99.4% success rate PoC. Used in RansomHub and Akira ransomware campaigns. Needs unprivileged user namespaces + nf_tables loaded. Affects 3.15–6.6.14.&lt;/p&gt;

&lt;p&gt;The other seven: Attack of the Vsock (CVE-2025-21756), Chronomaly (CVE-2025-38352), Packet Socket Race (CVE-2025-38617), OverlayFS SetUID Copy (CVE-2025-38352, CISA KEV), DirtyPipe (CVE-2022-0847), and DirtyCOW (CVE-2016-5195).&lt;/p&gt;




&lt;h2&gt;
  
  
  Updating the database
&lt;/h2&gt;

&lt;p&gt;The whole point of the config-driven design is that you shouldn't need to touch the script when new vulnerabilities drop. When a CVE gets a distro patch, update &lt;code&gt;fixed_versions&lt;/code&gt;. When something goes ITW, update &lt;code&gt;itw=yes&lt;/code&gt;. Add a new entry for a new CVE:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; cve_checks.conf &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;ENTRY&lt;/span&gt;&lt;span class="sh"&gt;'

cve_id=CVE-2025-XXXXX
name=Whatever it's called
cvss=8.1
severity=HIGH
check_type=compound
introduced=6.1
fixed_versions=6.6:6.6.50 6.12:6.12.5
itw=no
poc_public=yes
cisa_kev=no
subsystem=fs/btrfs
module_names=btrfs
mitigation=none
socket_af=none
socket_type=none
socket_proto=none
what=...
impact=...
exploit=...
rec=...
&lt;/span&gt;&lt;span class="no"&gt;ENTRY
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Running in Kubernetes
&lt;/h2&gt;

&lt;p&gt;Drop it into a pod directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nb"&gt;cp &lt;/span&gt;container_escape_audit.sh mynamespace/mypod:/tmp/audit.sh
kubectl &lt;span class="nb"&gt;cp &lt;/span&gt;cve_checks.conf mynamespace/mypod:/tmp/cve_checks.conf
kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; mynamespace mypod &lt;span class="nt"&gt;--&lt;/span&gt; bash /tmp/audit.sh &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cve-conf&lt;/span&gt; /tmp/cve_checks.conf &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--report&lt;/span&gt; /tmp/audit_report.txt
kubectl &lt;span class="nb"&gt;cp &lt;/span&gt;mynamespace/mypod:/tmp/audit_report.txt ./
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or as a Job that audits the cluster's default security context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;batch/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Job&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;container-escape-audit&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;restartPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Never&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;audit&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;alpine:latest&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sh&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;-c&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
              &lt;span class="s"&gt;apk add --no-cache bash curl python3 &amp;amp;&amp;amp; \&lt;/span&gt;
              &lt;span class="s"&gt;curl -sO https://raw.githubusercontent.com/liamromanis101/K8s-container_escape_audit/main/container_escape_audit.sh &amp;amp;&amp;amp; \&lt;/span&gt;
              &lt;span class="s"&gt;curl -sO https://raw.githubusercontent.com/liamromanis101/K8s-container_escape_audit/main/cve_checks.conf &amp;amp;&amp;amp; \&lt;/span&gt;
              &lt;span class="s"&gt;chmod +x container_escape_audit.sh &amp;amp;&amp;amp; \&lt;/span&gt;
              &lt;span class="s"&gt;./container_escape_audit.sh --json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; audit-job.yaml
kubectl &lt;span class="nb"&gt;wait&lt;/span&gt; &lt;span class="nt"&gt;--for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;complete &lt;/span&gt;job/container-escape-audit &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;120s
kubectl logs job/container-escape-audit
kubectl delete job container-escape-audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Job runs with whatever security context the cluster assigns by default — which is the point. You want to see what a workload running in your cluster can actually reach, not what it could reach if you gave it extra permissions.&lt;/p&gt;




&lt;h2&gt;
  
  
  CI integration
&lt;/h2&gt;

&lt;p&gt;The JSON output makes it straightforward to gate on CRITICAL findings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CRITICAL_COUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;./container_escape_audit.sh &lt;span class="nt"&gt;--json&lt;/span&gt; &lt;span class="nt"&gt;--no-report&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="s1"&gt;'[.findings[] | select(.severity=="CRITICAL")] | length'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CRITICAL_COUNT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"FAILED: &lt;/span&gt;&lt;span class="nv"&gt;$CRITICAL_COUNT&lt;/span&gt;&lt;span class="s2"&gt; critical escape vectors detected"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Requirements and limitations
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Requirements:&lt;/strong&gt; Bash 4.2+, Python 3. Everything else uses &lt;code&gt;/proc&lt;/code&gt;, &lt;code&gt;/sys&lt;/code&gt;, and standard POSIX tools. curl is optional (used for IMDS and Kubelet API checks).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it isn't:&lt;/strong&gt; this is a point-in-time audit, not continuous monitoring. It identifies attack surface — it doesn't exploit anything. A CRITICAL finding means the prerequisites for a known attack are present, not that you've been compromised. For continuous detection, pair it with Falco rules watching for writes to &lt;code&gt;release_agent&lt;/code&gt; and &lt;code&gt;core_pattern&lt;/code&gt;, AF_ALG socket creation from non-root processes, and LD_PRELOAD pointing to &lt;code&gt;/tmp&lt;/code&gt; or &lt;code&gt;/dev/shm&lt;/code&gt;.&lt;/p&gt;




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

&lt;p&gt;&lt;a href="https://creativecommons.org/licenses/by-nc/4.0/" rel="noopener noreferrer"&gt;CC BY-NC 4.0&lt;/a&gt; — free for non-commercial use with attribution. If you're using this as part of a paid engagement or commercial product, we're happy to discuss sponsorship: &lt;a href="https://github.com/sponsors/liamromanis101" rel="noopener noreferrer"&gt;github.com/sponsors/liamromanis101&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Feedback, issues, and PRs welcome. If you hit a false positive, a missed check, or a CVE that should be in the database, open an issue on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/liamromanis101/K8s-container_escape_audit" rel="noopener noreferrer"&gt;github.com/liamromanis101/K8s-container_escape_audit&lt;/a&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>kubernetes</category>
      <category>docker</category>
      <category>security</category>
    </item>
    <item>
      <title>GitHub Already Has More Engagement Than LinkedIn. It Should Do Something About It.</title>
      <dc:creator>Liam Romanis</dc:creator>
      <pubDate>Mon, 18 May 2026 08:27:57 +0000</pubDate>
      <link>https://dev.to/lromanis/github-already-has-more-engagement-than-linkedin-it-should-do-something-about-it-3k2e</link>
      <guid>https://dev.to/lromanis/github-already-has-more-engagement-than-linkedin-it-should-do-something-about-it-3k2e</guid>
      <description>&lt;p&gt;I published a CVE detection tool recently. Nothing unusual there.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/liamromanis101/CVE-2026-31431-Copy-Fail---Vulnerability-Detection-Script" rel="noopener noreferrer"&gt;https://github.com/liamromanis101/CVE-2026-31431-Copy-Fail---Vulnerability-Detection-Script&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On LinkedIn, the post got 385 impressions. A handful of likes, two comments, one of which was a recruiter, and one person clicked the link which might have been me testing it.&lt;/p&gt;

&lt;p&gt;The GitHub repo, in the last 14 days alone: 6,230 views, 3,254 unique visitors, 923 clones from 585 unique cloners. 22 stars, 22 watchers, 2 forks. People actually ran the code.&lt;/p&gt;

&lt;p&gt;Nobody clones a repo to be polite.&lt;/p&gt;

&lt;p&gt;That is not an edge case. That is the pattern, every time.&lt;/p&gt;




&lt;h2&gt;
  
  
  LinkedIn Shows You What People Claim. GitHub Shows You What People Do.
&lt;/h2&gt;

&lt;p&gt;LinkedIn is a performance platform. You write a headline, list your credentials, and hope the algorithm rewards you. The engagement is performative by design. Reactions, reposts, congratulations on your work anniversary from people you met once in 2019.&lt;/p&gt;

&lt;p&gt;GitHub is a proof-of-work platform. You push code. People clone it, break it, improve it, or ignore it. Nobody pretends to engage. The stars and forks are real signal because there is no social pressure to give them.&lt;/p&gt;

&lt;p&gt;This matters enormously if you are trying to build a company or find people worth building one with.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem GitHub Has Not Solved Yet
&lt;/h2&gt;

&lt;p&gt;Here is the thing: GitHub already has everything it needs to become the place where startups are formed.&lt;/p&gt;

&lt;p&gt;It has developers at the earliest stage of their best ideas. A commit is often the first tangible artifact of a company that will eventually be worth something. GitHub sees that before any investor does, before any accelerator does, before the founders themselves have put a name to what they are building.&lt;/p&gt;

&lt;p&gt;It has genuine engagement data. Not vanity metrics. Actual signals about who is working on what, who is collaborating with whom, and whose code other people trust enough to build on.&lt;/p&gt;

&lt;p&gt;It has a culture that LinkedIn cannot replicate. GitHub is not a strictly professional environment. It is not NSFW either. It sits in the space between the two, which means people are actually themselves on it. That is rare, and it is valuable.&lt;/p&gt;

&lt;p&gt;What it does not have is a product layer that uses any of this deliberately.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Think GitHub Should Build
&lt;/h2&gt;

&lt;p&gt;I wrote a full strategic proposal on this. The short version is: a founder matching and angel investor platform built on top of GitHub's existing engagement data.&lt;/p&gt;

&lt;p&gt;Call it GitHub Launchpad.&lt;/p&gt;

&lt;p&gt;The idea is not complicated. If you look at what somebody has built, what others have contributed to, who has consistently shown up across meaningful repositories, you have a far more reliable picture of a potential co-founder than any LinkedIn profile gives you. You are not reading a CV. You are reading a commit history.&lt;/p&gt;

&lt;p&gt;Add a structured layer for founders to signal that they are looking for collaborators or early investment, connect that to angels who want to back people before the pitch deck exists, and you have something no other platform can replicate. Because the proof-of-work layer is already there. It is just not being used for this.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Is a Disruption Opportunity, Not an Incremental Feature
&lt;/h2&gt;

&lt;p&gt;LinkedIn is not the natural home for early-stage startup formation. It is where companies post jobs once they already have fifty people. The seed-stage problem, finding the right co-founder, getting in front of the right angel before you have traction, has never been solved well.&lt;/p&gt;

&lt;p&gt;GitHub is already embedded in the moment before that. It is at the first commit. It sees the proof of capability that no other platform has access to.&lt;/p&gt;

&lt;p&gt;The opportunity to own startup formation from a position of genuine structural advantage is available now. That window will not stay open indefinitely as purpose-built competitors continue to develop.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Full Proposal
&lt;/h2&gt;

&lt;p&gt;I have written this up properly, with the product detail, the revenue model, and the competitive picture.&lt;/p&gt;

&lt;p&gt;If this is an idea worth discussing, I would rather discuss it somewhere people are actually building things.&lt;/p&gt;

&lt;p&gt;The full proposal is in the repo below. Thoughts welcome, especially from anyone who has tried to solve the co-founder matching problem and found the existing options wanting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/liamromanis101/github-launchpad" rel="noopener noreferrer"&gt;github.com/liamromanis101/github-launchpad&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tags: #startup #github #productivity #discuss&lt;/em&gt;&lt;/p&gt;

</description>
      <category>startup</category>
      <category>github</category>
      <category>career</category>
      <category>discuss</category>
    </item>
    <item>
      <title>I Built an Agentic Linux Security Tool. It Took Way More Iterations Than I Expected.</title>
      <dc:creator>Liam Romanis</dc:creator>
      <pubDate>Sun, 17 May 2026 23:53:51 +0000</pubDate>
      <link>https://dev.to/lromanis/i-built-an-agentic-linux-security-tool-it-took-way-more-iterations-than-i-expected-30nk</link>
      <guid>https://dev.to/lromanis/i-built-an-agentic-linux-security-tool-it-took-way-more-iterations-than-i-expected-30nk</guid>
      <description>&lt;p&gt;This started as a simple experiment: can you point an AI at a Linux system, have it collect forensic data, and get something more useful than a wall of text back?&lt;/p&gt;

&lt;p&gt;The answer, it turns out, is yes — but not in the way I originally thought, and not without a lot of iteration to get there.&lt;/p&gt;




&lt;h2&gt;
  
  
  How It Started
&lt;/h2&gt;

&lt;p&gt;The initial idea was straightforward. Run a bunch of forensic commands — process lists, open sockets, SUID binaries, kernel modules, log anomalies, the usual — pipe the output to Claude, and get a triage report back. Simple agentic loop. Collect, analyse, report.&lt;/p&gt;

&lt;p&gt;And that bit worked fine. Claude is actually pretty good at reading &lt;code&gt;ps auxf&lt;/code&gt; output and spotting things that look wrong. Better than I expected, honestly.&lt;/p&gt;

&lt;p&gt;The problem was what happened next. You'd get a list of findings and then... nothing. The same problem every security tool has. Here are some things that look suspicious. Good luck. The AI had done the easy bit and left you to figure out the hard bit on your own.&lt;/p&gt;

&lt;p&gt;That's not really agentic. That's just automation with a language model bolted on.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Interesting Problem
&lt;/h2&gt;

&lt;p&gt;What I actually wanted was an AI that could &lt;em&gt;investigate&lt;/em&gt; alongside you. Not just flag things, but help you work through whether a finding is real, what to do about it, and whether the remediation you're considering is going to cause more problems than it solves.&lt;/p&gt;

&lt;p&gt;The challenge is that investigation requires running commands on the live system. And if you're going to run commands on a live system based on AI suggestions, you absolutely cannot have those commands run automatically. The AI will get things wrong. The AI will suggest things that sound reasonable but aren't appropriate for your specific setup. The AI will, if you let it, suggest hardening measures that stop your system from booting.&lt;/p&gt;

&lt;p&gt;That last one happened. Not in a catastrophic way, but enough to make the point very clearly: you need a human in the loop, and that human needs to actually understand what they're approving.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Man-in-the-Loop Pattern
&lt;/h2&gt;

&lt;p&gt;What emerged after a lot of iteration is a batch investigation loop that goes like this:&lt;/p&gt;

&lt;p&gt;Claude analyses a finding and proposes a set of verification or remediation commands — typically three to six per round. These are displayed to you with a type badge (VERIFY, REMEDIATE, or INSTALL), a plain-English description of what the command does, the rationale for why it's useful, and — critically for anything that could affect system stability — a rollback command so you know how to undo it.&lt;/p&gt;

&lt;p&gt;You review all of them. You can deselect any you don't want. You can ask Claude questions about any command before approving it — "what does this actually do", "is there a safer alternative", "why is this necessary" — and get a direct answer in context.&lt;/p&gt;

&lt;p&gt;Then you click run. The approved commands execute sequentially, the output comes back, and Claude analyses everything together in one consolidated response rather than reacting to each command individually. If it needs more information, it proposes another batch. If it has enough to make a determination, it gives you a verdict and action buttons: confirm as false positive, keep as active finding, mark resolved.&lt;/p&gt;

&lt;p&gt;It took a lot of iterations to get this feeling natural. The early versions had Claude proposing one command at a time, which created an exhausting back-and-forth. Batch proposals with a single analysis pass work much better. The thread also has a tendency to grow unwieldy, so completed investigation rounds collapse into summaries.&lt;/p&gt;




&lt;h2&gt;
  
  
  The False Positive Problem
&lt;/h2&gt;

&lt;p&gt;Something that became obvious quickly: AI-generated findings are going to overlap with things you already know about and have decided to accept. Your custom SSH port. Your pentest tooling. The forensic agent's own token file sitting in /tmp.&lt;/p&gt;

&lt;p&gt;The tool has a false positive management system that goes beyond a simple whitelist. It uses fuzzy matching — a combination of token-based Jaccard similarity and longest-common-subsequence ratio — so that when Claude words a finding slightly differently on the next scan, it still gets suppressed. There's also a session-dismiss for things you want to acknowledge without permanently suppressing, and an FP audit workflow where Claude reviews your saved false positives and flags any that probably shouldn't be permanently suppressed because they could indicate real malicious activity in a different context.&lt;/p&gt;

&lt;p&gt;That last one is more useful than it sounds. "Orphaned PTY sessions" is a reasonable false positive if you left some terminals open. It's not a reasonable false positive to permanently ignore if it could also indicate someone else's session on your system.&lt;/p&gt;




&lt;h2&gt;
  
  
  Is It Any Good?
&lt;/h2&gt;

&lt;p&gt;Honestly, it's useful. More useful than I expected. For investigating real findings on a system you're responsible for, the investigation loop pattern genuinely helps — it keeps you from either ignoring things you should look at or taking action you don't understand.&lt;/p&gt;

&lt;p&gt;But let's be clear about what it isn't.&lt;/p&gt;

&lt;p&gt;It is not production ready. It is not comprehensively tested. The AI analysis is non-deterministic and occasionally wrong. The agent runs as root with minimal authentication over localhost — fine for personal use, not something you'd put in front of customers.&lt;/p&gt;

&lt;p&gt;Is it secure? No. It's a forensic tool that runs as root and executes commands you approve. Security is mostly your problem. Use it on systems you own, in environments you control, for purposes you understand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use it at your own risk.&lt;/strong&gt; If you blindly approve every command Claude suggests without reading them, you will eventually do something you regret. The tool tries to help — boot-risk warnings, required rollback instructions for kernel changes, explicit confirmation checkboxes before anything that could affect system stability — but it cannot protect you from yourself.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned About Agentic Development
&lt;/h2&gt;

&lt;p&gt;The most interesting thing about building this wasn't the security tooling. It was what the development process revealed about building agentic systems in general.&lt;/p&gt;

&lt;p&gt;The gap between "AI that does a task" and "AI that works alongside a human on a task" is much larger than it looks. The first is just automation. The second requires thinking carefully about where the human needs to be in the loop, what information they need to make good decisions, and how to present AI suggestions in a way that encourages understanding rather than blind acceptance.&lt;/p&gt;

&lt;p&gt;Getting that right took a lot more iteration than I expected. The first version had the AI running ahead too fast. Later versions were too cautious and required too many clicks for simple cases. The batch proposal pattern that ended up working is something I arrived at through trial and error, not design.&lt;/p&gt;

&lt;p&gt;That feels like the honest state of agentic development right now. The patterns are still being worked out.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where It Goes From Here
&lt;/h2&gt;

&lt;p&gt;It's open source under MIT: &lt;strong&gt;github.com/liamromanis101/SysForensics&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're interested in the agentic investigation pattern, want to add checks for other distributions, want to explore what commercial-grade security tooling built on this approach would look like, or just want to kick the tyres — get in touch. I'd be genuinely happy to make this a community project if there's interest.&lt;/p&gt;

&lt;p&gt;There's a lot of room to go further with this. Better context-awareness between findings, CVE cross-referencing, fleet management, proper reporting for auditors. The foundation is there. Whether it goes anywhere depends on whether other people find the approach interesting.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you do try it: read the commands before you approve them. That's the whole point.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>opensource</category>
      <category>security</category>
    </item>
  </channel>
</rss>
