InfoSec Taiwan 2026

AI Writes the Code.
Who Checks It?

Hundreds of billions of lines of code are being written by AI. AI now generates code faster than any of us can review it. The tooling to catch what it gets wrong has existed for twenty years, yet almost no one wired it up to the machine writing a million lines a week.

Track Application & AI Security
Level Expert
Focus CodeQL internals · sink models · rule-pack extension
Participate

AI Coding Tooling Security Survey

Two minutes, and it feeds the research. Scan the code, or open the link below.

QR code to the AI Coding Tooling Security Survey https://forms.gle/hwPD8eRESFCsaQW27
01

We ship code, we don't read

AI became a general-purpose technology. You describe what you want in plain English, and the tooling writes the code. The entire open-source software development lifecycle, from build to deployment to operations, collapses into the work of a single operator. One person now owns what used to take a team, and no one is reading the millions of lines in between.

"These days I don't read much code anymore. I watch the stream and sometimes look at key parts, but I gotta be honest - most code I don't read. I do know where which components are and how things are structured and how the overall system is designed, and that's usually all that's needed."

Peter Steinberger, creator of OpenClaw

Source: Shipping at Inference-Speed, Peter Steinberger

02

The Memory Lane of Coding Tools

How a decade of tooling led us to writing English instead of code.

2015

Editors, not assistants

VS Code, Sublime Text, Eclipse. You wrote every line yourself. The IDE highlighted syntax and little else.

2016

No-code & RPA

A parallel path was already open. Robotic process automation and enterprise no-code platforms like TIBCO let teams stand up microservices and apps with little hand-written code, just basic data design. The first taste of "describe it, don't code it."

2017

The Coursera generation

Andrew Ng's Python courses put a generation into Jupyter Notebooks: markdown, pip install, anaconda, IPython. Programming got a much wider front door.

2018

VS Code wins

The editor becomes the default surface for millions of developers, and the canvas that AI would later paint on.

2023

"The hottest new programming language is English"

Cursor and Codeium (Windsurf) fork the VS Code engine and bolt on AI chat and composer. As Andrej Karpathy put it, a post seen 12.7M times:

Andrej Karpathy tweet: The hottest new programming language is English
2025

"Vibe coding," word of the year

Collins Dictionary names vibe coding its word of the year. The practice is no longer fringe, it's the mainstream way software gets made.

Collins Dictionary word of the year: vibe coding
SECRETS

Secret detection

Catches hardcoded API keys, tokens, and credentials before they reach the repo, exactly what AI agents tend to copy or invent.

REVIEW

Security review

Security review reads a code path and reasons about whether it is actually exploitable, not just pattern matching.

As of July 2026, the OpenClaw ecosystem (the agent plus components like Crabbox) shows up 863 times in the GitHub Advisory Database and 545 times in the CVE record, on a project barely six months old.

Source: GitHub Advisory Database (query: OpenClaw) and CVE.org, counts as of July 2026

03

AI is already in the pipeline.

Signals from GitHub, 2025 to 2026: who reviews the code, who opens the pull requests, and who pushes to build.

AI coding assistants now perform 60% of bot PR reviews on GitHub, up from about 20% a year earlier.

AI starts writing code

Top PR authors · Q2 2025

  1. #1Dependabot2.3m
  2. #2Pull1.5m
  3. #3Renovate780K

The real number is higher, think Claude Code or Cursor authored commits.

Where AI builds applications

Top pushes · Q2 2025

  1. #1GitHub Actions5.9m
  2. #2SWA Runner App3.9m
  3. #3Renovate2.5m

Source: The State of Coding AI on GitHub (Star History)

04

Build, build build!

Make coding cheaper and you don't get less code, you get vastly more of it. Economists have a name for this: Jevons paradox.

The tools multiplied, and each one lowered the cost of producing code by another order of magnitude.

Source: Architecture Overview

Common Weakness Enumeration-78

Command injection

The program lets user input become part of a system command, and the attacker can sneak in extra commands.

CWE-78: Improper Neutralization of Special Elements used in an OS Command

Scope: Confidentiality, Integrity, Availability, Non-Repudiation

The impact: Execute Unauthorized Code or Commands; DoS: Crash, Exit, or Restart; Read Files or Directories; Modify Files or Directories; Read Application Data; Modify Application Data; Hide Activities.

Attackers could execute unauthorized operating system commands, which could then be used to disable the product, or read and modify data for which the attacker does not have permissions to access directly. Since the targeted application is directly executing the commands instead of the attacker, any malicious activities may appear to come from the application or the application's owner.

CWE-78 OS Command Injection diagram: attacker sends $userName; DELETE ALL FILES, vulnerable script concatenates it into LIST /home/$userName, resulting in LIST /home/;DELETE ALL FILES executed on the server
Source: MITRE CWE-78
C · list a user's home directory
#include <stdio.h>
#include <stdlib.h>

void list_home(char *user) {
    char cmd[256];
    sprintf(cmd, "ls /home/%s", user);
    system(cmd);
}

int main() {
    list_home("alice");
    // runs:  ls /home/alice
}
C · same code, attacker input
#include <stdio.h>
#include <stdlib.h>

void list_home(char *user) {
    char cmd[256];
    sprintf(cmd, "ls /home/%s", user);
    system(cmd);
}

int main() {
    list_home("alice; rm -rf /home");
    // runs:  ls /home/alice; rm -rf /home
    //        lists, then deletes  ☠
}

Asleep at the Keyboard? Assessing the Security of GitHub Copilot's Code Contributions

Researchers asked GitHub Copilot to write three ordinary programs. Each one takes something the user types and hands it to the system.

  1. Test 1 Ask the AI to write code that lists the files in a folder the user names.
  2. Test 2 Ask the AI to write code that looks up a user by their username.
  3. Test 3 Ask the AI to write code that pings a web address the user types in.

The majority of Copilot's suggestions were vulnerable. For Test 1, the exact program above, every option it generated was vulnerable, and it was most confident in the insecure code.

Source: Asleep at the Keyboard? (arXiv:2108.09293)

05

The awareness gap

We surveyed AI practitioners, developers, security engineers, engineering managers, and students. The marketing and the experience on the ground are two different things.

Low
awareness of what security capability is actually available in their own tools
Low
confidence that an AI coding tool can catch its own mistakes
?
can't tell built-in from third-party from simply not-available
16th-century woodcut of a woman throwing a baby out with the bathwater
The original idiom, ~1512.
06

Throwing the baby out with the bathwater

The tool that catches AI's command injection is not new. It is static analysis, and it has run for twenty years. This section is one narrow, provable claim: static analysis plus taint analysis plus a current sink list catches this class of bug. The baby was never broken, teams just stopped pointing it at the code.

Static analysis reads code without running it, for anything from types to bugs to security. The security slice of it is SAST, Static Application Security Testing. Inside SAST, taint analysis follows the data, and a rule pack extends what it catches. That chain is the rest of this section.

Static analysis Reads code without running it types · linting · bugs · security SAST Static Application Security Testing Taint analysis Source to sink dataflow Sink list The calls it flags Rule pack Your custom sinks extends
Static analysis contains SAST, SAST contains taint analysis, and a rule pack extends the sink list.
How SAST works

Read the code, flag what looks dangerous

SAST reads code without running it and flags risky patterns. Two engines dominate the field:

  1. Semgrep Open-source, pattern-oriented. The name is short for "semantic grep": you write regex-style patterns in YAML and it matches them against the code's structure, not just its text. Fast, no database to build.
  2. CodeQL Built by GitHub. It treats code as data in a database and you query it. More powerful for deep data-flow, slower than a pattern scan.

Both Semgrep and CodeQL are Static Application Security Testing (SAST) tools, and both feature taint analysis capabilities.

How taint analysis works

Follow the untrusted data, i.e. Command injection, to a dangerous sink, i.e. ;delete all file

A taint engine models the code as a data-flow graph and control-flow graph over its abstract syntax tree (AST), then traces whether user input can reach a sink. It runs in either direction:

  1. source → sink Start from every input and follow it forward. Thorough, but slower.
  2. sink → source Start from the dangerous call and walk backwards. Faster, and how most command-injection queries run.

Either direction, it only reports a flow that reaches a sink on its list. The quality of that sink list decides what gets found. Miss the sink, miss the bug.

DEFAULT tainted input _execvp( ) Injection ships scanner never looked SCANNER'S LIST exec() spawn() system() _execvp() ✗ not on the list WITH A RULE PACK tainted input _execvp( ) Injection caught now on the list SCANNER'S LIST exec() spawn() system() _execvp() ✓ added by rule pack
A scanner only flags calls on its list. _execvp was missing, so the injection shipped. A rule pack adds it, no change to the engine, and the same injection is caught.
The gap

_execvp was not on the list

CodeQL's command-execution sinks live in one hand-maintained library, CommandExecution.qll. It enumerates the POSIX exec* family and the Windows _spawn* / _wexec* variants. _execvp was not among them, so data flowing into it was invisible to the analysis. Reported and fixed in GitHub discussion #15963.

If a sink is not on the list, the engine cannot find it. The engine was never broken, the list was incomplete.

CodeQL · CommandExecution.qll (the sink list)
// known command-execution functions, hand-maintained
// cpp/ql/lib/.../security/CommandExecution.qll

["execl", "execle", "execlp",
 "_spawnl", "_wexecl", "_wspawnl", ...]

["execv", "execvp", "execve", "fexecve",
 "_execvp", "_spawnv", "_wexecv", ...]
//  ^ _execvp: missing before, added after #15963
Close the gap

Rule packs: teach the scanner your sinks

A rule pack extends a scanner past its defaults to what is dangerous in your environment. With LLMs you can add sinks you spot in the wild, on the fly. Semgrep uses YAML patterns, CodeQL adds a query, Fortify ships XML Rulepacks, and a sink can be extended with a one-line JSON entry.

Semgrep YAML · raptor-command-injection.yaml
rules:
  - id: raptor-command-injection
    languages: [c, cpp]
    severity: ERROR
    message: OS command built from externally-influenced input.
    patterns:
      - pattern-either:
          - pattern: system(...)
          - pattern: popen(...)
          - pattern: p2open(...)
          - pattern: wordexp(...)
      - pattern-not: $_("...", ...)
Extended sink · add execv on the fly (JSON)
{
  "type": "Sink",
  "fn_list": [
    {
      "fn_name": "^(execv)$",
      "is_regex": 1,
      "match_type": [
        "find_param_0",
        "find_param_1"
      ]
    }
  ]
}
Proof it works

When the baby works

In 2019 the research team ran a known SAST scanner on NGINX 1.4.0 for CVE-2013-2028 with no custom rule pack. It found nothing. We modeled the taint flow, added a custom rule pack, and the same scanner found it. The engine wasn't broken, the rule pack was empty. Along the way we also hit real limits in the engine itself.

Not just us

Rule-pack quality is a field, not a footnote

Maintained custom rule packs and academic work on rule-pack design:

Expert deep dive

The sink model, in full

CodeQL's command-execution sinks are enumerated by name in CommandExecution.qll. The complete POSIX exec* family plus Windows variants. If a name is not on these lists, no flow into it is ever reported.

CommandExecution.qll · varargs exec family
["execl", "execle", "execlp",
 // Windows
 "_execl", "_execle", "_execlp", "_execlpe",
 "_spawnl", "_spawnle", "_spawnlp", "_spawnlpe",
 "_wexecl", "_wexecle", "_wexeclp", "_wexeclpe",
 "_wspawnl", "_wspawnle", "_wspawnlp", "_wspawnlpe"]
CommandExecution.qll · array exec family
["execv", "execvp", "execvpe", "execve", "fexecve",
 // Windows variants
 "_execv", "_execve", "_execvp", "_execvpe",
 "_spawnv", "_spawnve", "_spawnvp", "_spawnvpe",
 "_wexecv", "_wexecve", "_wexecvp", "_wexecvpe",
 "_wspawnv", "_wspawnve", "_wspawnvp", "_wspawnvpe"]
//                    ^ _execvp was absent until #15963
ExecTainted.ql · the sink predicate
// the sink lives in the reusable library, not the query
predicate isSinkImpl(DataFlow::Node sink, Expr command, string callChain) {
  command = sink.asIndirectArgument() and
  shellCommand(command, callChain)   // <- CommandExecution.qll
}
ExtendedExecTainted.ql · extend without patching the stdlib
predicate extendedShellCommand(Expr cmd, string callChain) {
  shellCommand(cmd, callChain)          // original sinks
  or
  exists(FunctionCall fc |             // + _execvp
    fc.getTarget().getName() = "_execvp" and
    fc.getArgument(0) = cmd and callChain = "_execvp"
  )
  or
  exists(FunctionCall fc |             // + your own sink
    fc.getTarget().getName() = "myDangerousExec" and
    fc.getArgument(1) = cmd and callChain = "myDangerousExec"
  )
}

No one wired these up to the AI tool that just wrote a million lines of code. Most teams still haven't.

How SCA works

Software Composition Analysis has depth too

Most SCA tools stop at the first level: a dependency matches a known CVE. The gap between "a CVE exists" and "this CVE can hurt you" is a maturity curve of its own:

  1. Level 1 · CVE Detection Match dependencies against known-vulnerability databases. What Dependabot and most SCA tools already do.
  2. Level 2 · Reachability Analysis Does your first-party code actually call the vulnerable function? Most CVEs in a dependency tree are never reached.
  3. Level 3 · Exploitability Reachable is not the same as exploitable. This layer checks attack prerequisites, EPSS scores, and known exploits.
  4. Level 4 · Runtime Evidence Confirms the vulnerable path actually runs in production, not just on paper.

AISLE is a working example at Levels 2 and 3: it traces the dependency chain into first-party code and synthesizes exploitability signals instead of guessing.

Source: How AISLE Unifies Detection and Remediation at Scale

Survey pie chart: which AI coding tool do you use most frequently: Claude Code 70%, Cursor 10%, Claude Code 10%, GitHub Copilot 10%
AI Coding Agent Security Survey 2026

Claude Code dominates at 70%

Even among people who use these tools every day, some didn't know the security capabilities existed at all. (Preliminary research.)

$ In practice: Semgrep Guardian wired into Claude Code

Wire it in

Semgrep Guardian

The Claude Code plugin runs security analysis right inside the CLI: SAST checks applied to code as the agent writes it, not months later in a pen test.

07

The ship has sailed

AI continues to write more code. If you are the single operator, you are also the security team, and it now has to cover the autonomous agents writing and shipping your code. Here is where to start, without slowing down.

Extend it

Custom rulepackers

Encode your team's institutional knowledge, the sinks and patterns that are dangerous in your environment, and apply it to every line the agent emits.

Close the gap

Know your tools

Audit what security capability your AI coding stack actually ships with. Built-in? Third-party? Or not there at all? Assume nothing from the marketing.

  1. SAST Static Application Security Testing: static analysis aimed at security. CodeQL, Semgrep.
  2. SCA Inventories open-source dependencies, flags known CVEs, generates an SBOM. Dependabot, GitHub Advisory Database.
  3. Review Security review that reasons about exploitability, not just patterns.
  4. Secrets Catches hardcoded keys, tokens, and credentials before they reach the repo.

SAST. SCA. Taint analysis. Custom rules. The baby.

Wire it into the agent that writes your code, so it checks what you don't read.
Don't throw the baby out because AI is writing the code now. That's exactly when you need it most.