<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://falcorocks.github.io/blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://falcorocks.github.io/blog/" rel="alternate" type="text/html" /><updated>2026-03-25T15:29:03+00:00</updated><id>https://falcorocks.github.io/blog/feed.xml</id><title type="html">blog</title><entry><title type="html">Benign Prompt Injection: AGENTS.md as a Security Lever</title><link href="https://falcorocks.github.io/blog/2026/03/25/benign-prompt-injection.html" rel="alternate" type="text/html" title="Benign Prompt Injection: AGENTS.md as a Security Lever" /><published>2026-03-25T00:00:00+00:00</published><updated>2026-03-25T00:00:00+00:00</updated><id>https://falcorocks.github.io/blog/2026/03/25/benign-prompt-injection</id><content type="html" xml:base="https://falcorocks.github.io/blog/2026/03/25/benign-prompt-injection.html"><![CDATA[<p>AI coding agents are writing a growing share of production code. Whether it’s Cursor, Copilot, or Claude Code, developers are increasingly pair-programming with models that generate, refactor, and ship real software. Naturally, this created a problem: agents don’t know your codebase conventions, your architectural decisions, or your dependency preferences. They go wild. The community’s answer was <code class="language-plaintext highlighter-rouge">AGENTS.md</code>, a repo-level instruction file that tells agents how to behave. It’s a pragmatic, elegant solution. Write your conventions once, and every agent session picks them up automatically. No more repeating “we use <code class="language-plaintext highlighter-rouge">slog</code> not <code class="language-plaintext highlighter-rouge">log</code>” in every prompt.</p>

<p>The security community has spent the last couple of years sounding the alarm on prompt injection. Rightly so. Untrusted input that manipulates an agent’s behavior is a serious threat. But <code class="language-plaintext highlighter-rouge">AGENTS.md</code> is prompt injection by design. It’s a file that deliberately shapes the agent’s output on every interaction. The difference is that the repo owner controls it. This is worth pausing on. If you already have a mechanism that influences every line of agent-generated code in your repository, why limit it to tabs-vs-spaces and import ordering? The same channel that nudges style can nudge security.</p>

<p>Developers now ubiquitously use agents to write code, and the results are notoriously riddled with security issues. One only needs to glance at the OpenClaw discourse to see the growing catalog of vulnerabilities shipped by agent-generated code. This makes the opportunity even more compelling. Consider what becomes possible when security teams start contributing to <code class="language-plaintext highlighter-rouge">AGENTS.md</code>. A single line like “never interpolate user input into SQL queries; always use parameterized statements” will be followed by the agent on every code generation, across every contributor, in every session. No training required. No linter to configure and maintain. No link to a wiki that nobody reads. You can go further: “always pin dependency versions to exact SHAs,” “never store secrets in environment variable defaults,” “always validate and sanitize API inputs before processing.” These aren’t aspirational guidelines sitting in a Confluence page. They’re instructions that the agent applies at the exact moment code is being written. Security teams get to push guardrails into the development loop at the point of highest leverage, before the code even exists in a PR.</p>

<p>The real power here is organizational. Create a company-wide <code class="language-plaintext highlighter-rouge">AGENTS.md</code> template that ships with every new repository, and you’ve just given every engineer in the company the same security baseline. Junior developers, contractors, new hires on day one: they all benefit because the agent enforces the rules regardless of the human’s experience level. This sidesteps the classic adoption problem where security tooling gets ignored, misconfigured, or quietly removed from CI because it’s “too noisy.” And if <code class="language-plaintext highlighter-rouge">AGENTS.md</code> becomes a security control, treat it like one. Add your security team to the CODEOWNERS file for it. Now any change to those guardrails requires security review before it merges. You get a governed, auditable control point in every repository, maintained through the same pull request workflow developers already use daily.</p>

<p>To be clear, <code class="language-plaintext highlighter-rouge">AGENTS.md</code> is not a replacement for proper security tooling. Static analysis, dependency scanning, secret detection, admission controllers: all of those still matter. Agents can misinterpret instructions, hallucinate exceptions to rules, or simply be overridden by a developer who knows better (or thinks they do). But as a layer in a defense-in-depth strategy, it’s surprisingly high leverage for near-zero friction. The best security controls are the ones people actually use. This one requires no behavior change at all. Developers keep prompting their agents exactly as before; they just get safer code out the other end. Sure, developers can bypass <code class="language-plaintext highlighter-rouge">AGENTS.md</code> entirely. But most engineers aren’t trying to be malicious; they’re trying to ship as quickly as possible. A guardrail that’s already in the path of least resistance will catch the vast majority of cases. In a world where agents write more code every quarter, shaping how they write it isn’t just convenient. It’s a security imperative.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[AI coding agents are writing a growing share of production code. Whether it’s Cursor, Copilot, or Claude Code, developers are increasingly pair-programming with models that generate, refactor, and ship real software. Naturally, this created a problem: agents don’t know your codebase conventions, your architectural decisions, or your dependency preferences. They go wild. The community’s answer was AGENTS.md, a repo-level instruction file that tells agents how to behave. It’s a pragmatic, elegant solution. Write your conventions once, and every agent session picks them up automatically. No more repeating “we use slog not log” in every prompt.]]></summary></entry><entry><title type="html">Software Supply Chain Build Provenance: a primer</title><link href="https://falcorocks.github.io/blog/2025/11/28/provenance-primer.html" rel="alternate" type="text/html" title="Software Supply Chain Build Provenance: a primer" /><published>2025-11-28T00:00:00+00:00</published><updated>2025-11-28T00:00:00+00:00</updated><id>https://falcorocks.github.io/blog/2025/11/28/provenance-primer</id><content type="html" xml:base="https://falcorocks.github.io/blog/2025/11/28/provenance-primer.html"><![CDATA[<p>If you work in software security, you’ve likely watched the rise of supply chain attacks with growing concern. From SolarWinds to Codecov, attackers have realized it’s often easier to compromise the build pipeline than the application itself. When a malicious package sneaks into your dependency tree or a compromised artifact lands in production, our traditional security controls often stay silent until it’s too late.</p>

<p>Provenance attestation is the answer to this problem. It provides cryptographically verifiable metadata about how, when, where, and from what your software was built. Think of it as a “birth certificate” for your artifacts. In this article, we’ll look at what provenance attestation is, why the old tools aren’t enough, and how it changes the software supply chain security game.</p>

<h2 id="why-provenance">Why Provenance?</h2>

<p>We’re all familiar with authenticity and integrity. When you verify a digital signature or check a file’s hash, you’re asking two questions: “Did this come from who I think it came from?” and “Has it been tampered with?” These are essential checks. They tell you to trust an artifact based on its current state. But what if the origin itself is the problem?</p>

<p>Imagine an attacker compromising your build server to inject malicious code. The resulting binary will be signed with your valid key (authenticity check: pass) and won’t change after signing (integrity check: pass). But it’s still malicious. Authenticity and integrity confirm the artifact’s state, but they can’t answer the deeper question: “What is the true origin of this artifact?”</p>

<p>This is the gap provenance attestation fills. While authenticity asks “who signed this?”, provenance asks “where did this really come from?” It doesn’t replace the old checks; it completes them. Together, they give you the full picture: you know who created it, that it hasn’t changed, and exactly how it came to be.</p>

<h2 id="what-is-provenance">What is Provenance?</h2>

<p>Let’s strip this down. Building software is just a transformation. You put source code in, you get an artifact out. Whether you’re running <code class="language-plaintext highlighter-rouge">go build</code> to create a binary, <code class="language-plaintext highlighter-rouge">docker build</code> to create a container image, or <code class="language-plaintext highlighter-rouge">npm publish</code> to package a library, the pattern is identical. This magic usually happens on a platform which usually means a CI system like GitHub Actions or Jenkins.</p>

<p>The industry standard for capturing this process is the in-toto provenance specification, part of the SLSA (Supply-chain Levels for Software Artifacts) framework. SLSA (pronounced “salsa”) is a ladder of security guarantees. At Level 2 and above, it combines three critical things into one signed document:</p>
<ol>
  <li><strong>Authenticity</strong>: A cryptographic signature verifying <em>who</em> created the document.</li>
  <li><strong>Integrity</strong>: A digest of the artifact, so you can verify it matches <em>what</em> was built.</li>
  <li><strong>Provenance</strong>: The detailed story of <em>where</em>,<em>how</em> and <em>from what</em> it was built.</li>
</ol>

<p>Here’s what a real one looks like (simplified for sanity):</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://in-toto.io/Statement/v1"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"subject"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="w">
    </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"myapp"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"digest"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="nl">"sha256"</span><span class="p">:</span><span class="w"> </span><span class="s2">"abc123..."</span><span class="p">}</span><span class="w">
  </span><span class="p">}],</span><span class="w">
  </span><span class="nl">"predicateType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://slsa.dev/provenance/v1"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"predicate"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"buildDefinition"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"buildType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://actions.github.io/buildtypes/workflow/v1"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"externalParameters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"workflow"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"repository"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://github.com/example/myapp"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"path"</span><span class="p">:</span><span class="w"> </span><span class="s2">".github/workflows/build.yml"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"ref"</span><span class="p">:</span><span class="w"> </span><span class="s2">"refs/heads/main"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"internalParameters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"github"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"event_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"push"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"repository_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"123456"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"runner_environment"</span><span class="p">:</span><span class="w"> </span><span class="s2">"github-hosted"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"resolvedDependencies"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="w">
        </span><span class="nl">"uri"</span><span class="p">:</span><span class="w"> </span><span class="s2">"git+https://github.com/example/myapp@refs/heads/main"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"digest"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="nl">"sha1"</span><span class="p">:</span><span class="w"> </span><span class="s2">"def456..."</span><span class="p">}</span><span class="w">
      </span><span class="p">}]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"runDetails"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"builder"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://github.com/actions/workflows/build.yml@refs/heads/main"</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"metadata"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"invocationId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"run-123"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"startedOn"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-01-15T10:30:00Z"</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>The git commit digest tells you exactly which version of the source code was used, eliminating ambiguity about what code went into the build. The branch and repository information show where the code came from, allowing you to enforce policies like “only accept builds from the protected main branch.” The builder infrastructure information reveals whether the build ran on a GitHub-hosted runner or a self-hosted runner.</p>

<h2 id="provenance-based-verification-model">Provenance Based Verification Model</h2>

<p>Here is the hard truth: verifying integrity and authenticity is necessary, but it’s not enough. Those checks only tell you that you have a valid, unaltered file signed by a trusted key. They answer “Is this legit?” but fail to answer “Should I trust it?” To answer that, we need to compare the provenance metadata against our <strong>expectations</strong>. Think of these expectations as an authorization barrier for your code.</p>

<p>Consider a malicious insider with access to your signing keys. They could build a malicious artifact from a personal feature branch and sign it. Cryptographically, it’s perfect. But the provenance metadata tells the real story: the build came from <code class="language-plaintext highlighter-rouge">feature/malicious-branch</code> instead of <code class="language-plaintext highlighter-rouge">main</code>. A simple policy check would catch this immediately, rejecting the artifact despite its valid signature.</p>

<p>This verification model works well in these scenarios:</p>

<ul>
  <li><strong>Deployment admission control</strong>: Ensuring only approved code runs in production.</li>
  <li><strong>Transitive trust</strong>: Verifying the security of base images and dependencies.</li>
  <li><strong>Privileged tooling</strong>: Checking that the tools used in sensitive operations are trustworthy.</li>
</ul>

<p>In deployment admission control you can enforce strict origin policies. For example, you might only allow artifacts built from specific repositories, or demand that they come from protected branches that require changes to go through PRs with mandatory code review. A Kubernetes admission controller can verify these details before allowing a pod to start. It could check that a container image was built from the main branch using a GitHub-hosted runner. Even if an attacker signs a malicious image, the provenance will reveal the unauthorized branch, and the admission controller will reject it.</p>

<p>When building on top of other artifacts, provenance enables transitive trust verification. Consider a Dockerfile that uses <code class="language-plaintext highlighter-rouge">FROM base-image:latest</code>. Before building your application, you can verify that the base image’s provenance shows it came from the expected origin. This prevents supply chain attacks where a compromised base image propagates into your builds. The provenance attestation of the base image becomes part of your build’s dependency chain, allowing you to trace the entire lineage of your final artifact back to trusted sources.</p>

<p>Another critical scenario is running tools with high privileges. Whenever you execute operations that are sensitive, such as authentication or system configuration, you should verify that the tools themselves come from expected origins. For example, before using the Cosign CLI to sign artifacts or verify attestations, you should check that the Cosign binary itself has provenance showing it was built from the official Sigstore repository. This is especially important in automations, for instance in CI workflows run by bots.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Provenance attestation is more than just a security upgrade; it’s a mindset shift. We’re moving from a world where we blindly trust signatures to one where we verify origins. It’s the difference between checking an ID card and verifying a background check. By combining authenticity, integrity, and provenance, we can build authorization barriers that reject even correctly signed artifacts if they don’t come from trusted processes. As attackers get smarter, our trust models need to get stricter. The question isn’t “who signed this?”, but “how was this built?”.</p>

<p>However, provenance alone isn’t a silver bullet. If the build platform generating the provenance is compromised, it can be tricked into lying. In the next article, we’ll explore attacks against provenance generation itself and how higher SLSA levels help mitigate them.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[If you work in software security, you’ve likely watched the rise of supply chain attacks with growing concern. From SolarWinds to Codecov, attackers have realized it’s often easier to compromise the build pipeline than the application itself. When a malicious package sneaks into your dependency tree or a compromised artifact lands in production, our traditional security controls often stay silent until it’s too late.]]></summary></entry></feed>