DEV Community

Cover image for "Please perform a comprehensive security audit" - and why it doesn't work
FeDEX
FeDEX

Posted on • Originally published at aisafe.io

"Please perform a comprehensive security audit" - and why it doesn't work

Introduction

Hi there! We are AISafe Labs, a crew of security researchers with only one goal: making security accessible to everyone!

We've all witnessed the recent rise of LLMs that has been nothing short of revolutionary. We're reaching a point where the power of a single prompt surpasses any expectation. From generating entire codebases to replacing entire workflows, the bar keeps getting raised. Building this much trust in the power of a prompt naturally raises the question: "Can you secure your application with a single prompt?"

In this article, we put that question to the test. We will assess how far a single prompt can take you when it comes to securing a real application, then compare that to a more targeted prompt armed with domain knowledge and specific instructions, and finally, stack both against AISafe, our specialized source code auditing platform. Same codebase, same goal, will the difference be significant or negligible?

Product Findings Vulnerabilities with direct impact Weaknesseses without direct impact Accepted risk False positives
Claude Code 24 1 8 11 4
Claude Code w Skills 21 0 5 2 14
Claude Code /security-review 1 0 1 0 0
Codex 4 2 2 0 0
Codex Security 44 6 18 8 11
AISafe 20 9 4 7 0

Let's begin! 🍿

The Target

Gokapi is an open source file hosting application with around 2.7k stars on GitHub, 1.5M Docker pulls, and roughly 35k lines of code (Go & JavaScript).


Preview of Gokapi app

It sits in a sweet spot for our case study: large enough to present a complex threat model and with modern web app features such as file handling, API keys, and user permissions with different access levels, yet small enough to audit in a reasonable time.

All experiments were run against commit a7c4273b819b8a48f85b866e1803632c089f60a2, pushed on March 1st, 2026.

Anthropic

Claude Code

Our first candidate is Claude Code, running Opus 4.6 with high effort. We are going to start with the most simple setup possible: a single prompt, just the kind that it's being recommended on social media every other day.

To begin, we drop Claude Code into the Gokapi folder, and type "Please perform a security audit of this repository". It will spin up a few agents and start looking at different parts of the codebase:

A few minutes later, we are already looking at 24 findings 👀

4 criticals? Let's break them down!

The first two are "SHA-1 Password Hashing" and "Zero-Filled Nonces in AES-GCM Encryption".

While Gokapi does use SHA-1 for password hashing, and sure, that's definitely not a great idea, still, it is a weakness rather than an exploitable vulnerability. A Low severity finding at best.

The "zero-filled nonce" finding sounds scary until you take a closer look and realize that the nonce gets handed off to the sio-go library, which appends its own nonce on top, and the server also uses per-file keys. Again, the real-world impact is practically none.

The other two critical findings flag "XSS in templates" and an "open redirect". Unfortunately both are false positives: the templates rely on Go's html/template package which handles sanitization out of the box, and on the other hand the redirect URL is only ever set by the instance admin.

Four critical vulnerabilities reported, but no real security issues were identified.

Moving on to the high impact findings. Three of the seven are about missing cookie attributes and security headers like HttpOnly, Secure, SameSite, Content-Security-Policy, and X-Frame-Options. Not a bad report, but again, not really vulnerabilities either. Another finding flags missing CSRF protection, which has had generally pretty limited impact ever since SameSite=Lax became the default.

Finally, there is one finding that points us to something actually vulnerable: a "Header Injection" issue, where file.Name is potentially attacker-controlled:

w.Header().Set("Content-Disposition", "attachment; filename=""+file.Name+""")
Enter fullscreen mode Exit fullscreen mode

However, the impact is overstated. Go validates headers and blocks CRLF injection, so the possible corruption is limited only to the header.

Lastly, the final two high impact findings describe header-based auth, which is intentional by design, and a missing TLS configuration that turns out to be another false positive.

The mediums are all theoretical with no meaningful real-world impact.

Summary:

At first glance, the results might look like a success. Twenty-four findings sound impressive, and the report looks thorough. But, once you dig in you will realize that while some of the findings are fix-worthy, most of them have little to no real impact.

We've tried several other prompt variations, but the results were largely the same.

Claude Code \w bug-class specific prompts

If you blame the generic prompt for the previous results, don't worry because in this experiment we will up our prompt game and try to hunt more specific: "Look for business logic bugs and permission problems".

This time we only got 10 total findings, and things are already looking more promising. The single critical finding points to a function that runs every hour to clean up file requests belonging to deleted users:

func cleanInvalidFileRequests() {
    users := getUserMap()
    for _, fileRequest := range database.GetAllFileRequests() {
        _, exists := users[fileRequest.UserId]
        if !exists {
            files := database.GetAllMetadata()
            for _, file := range files {
                if file.UploadRequestId == fileRequest.Id {
                }
                DeleteFile(file.Id, true)
            }
            database.DeleteFileRequest(fileRequest)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

There is an obvious typo here, but a pretty painful one. If it encounters a file request whose owner no longer exists, it will delete every single file from the application. That said, the trigger condition is narrow just as the comment sitting right above the function explicits:

// cleanInvalidFileRequests removes file requests and the associated files from the database if their associated owner is not a valid user.
// Normally this should not be a problem, but if a user was manually deleted from the database,
// this could cause issues otherwise.
Enter fullscreen mode Exit fullscreen mode

This only fires when a user is deleted directly from the database, not through the application's logic. Still this is a real bug, just not as alarming as the title sounds. Medium impact would be a more appropriate classification.

Two other genuine findings also made it through:

  1. A user without UserPermGuestUploads permission can generate a temporary API key with ApiPermManageFileRequests and use it to create File Requests they should never have been able to create. A solid finding that breaks a real permission assumption.

  2. A copy-paste mistake in apiUploadRequestListSingle that checks UserPermDeleteOtherUploads on a function meant for listing, not deleting:

func apiUploadRequestListSingle(w http.ResponseWriter, r requestParser, user models.User) {
    request, ok := r.(*paramURequestListSingle)
    if !ok {
        panic("invalid parameter passed")
    }

    uploadRequest, ok := filerequest.Get(request.Id)
    if !ok {
        sender(w, http.StatusNotFound, errorcodes.NotFound, "FileRequest does not exist with the given ID")
        return
    }
    if uploadRequest.UserId != user.Id && !user.HasPermission(models.UserPermDeleteOtherUploads) {
        sender(w, http.StatusUnauthorized, errorcodes.NoPermission, "No permission to delete this upload request")
        return
    }
    result, err := json.Marshal(uploadRequest)
    helper.Check(err)
    _, _ = w.Write(result)
}
Enter fullscreen mode Exit fullscreen mode

A valid bug, though with limited security implications in practice.

The rest of the findings circle back to familiar territory: file passwords stored as hashed values in a short-lived browser cookie (which appears intentional), a missing HttpOnly flag, and a non-constant-time comparison.

Summary:

This technique shows improvements over the previous one, but not all bug-class specific prompts are equal, when we try “please report all XSS vulnerabilities in this app” the results are much worse, out of 5 reported vulnerabilies, 4 are false-positives and the remaining one is an admin-triggered self-XSS in expired hotlinks, which turned out to be false positive. However, it actually missed a stored XSS vulnerability that we will later discuss when a different tool discovers it.

Claude Code with Skills

Next, what if instead of just prompting more specific, we actually empower Claude with top-tier infosec knowledge? Introducing Claude Skills - specialized, prompt-based instructions and tools that extend the LLM's capabilities. We will explore pairing Claude Code with Trail of Bits's open source security Skills and see what additional value it brings.

We enable a handful of skills, relevant to the target we are auditing:

  • audit-context-building,
  • fp-check,
  • insecure-defaults,
  • sharp-edges,
  • static-analysis,
  • testing-handbook-skills,
  • variant-analysis

then ask for a "comprehensive security audit".

Claude picked up the audit-context-building:audit-context skill and got to work. The results, though, tell a familiar story. The one improvement is that the fp-check skill lets us ask Claude to filter out its own false positives, trimming the report from 21 findings down to 7.

Of those 7, the only new addition is a world-readable config file. Everything else we had already seen before.

Summary:

While the vulnerability discovery process itself did not improve much, the benefit of having a false positive detection skill is noticeable: the report took less time to triage and the findings that made it through are far more actionable.

Claude Code /security-review

We cannot wrap up our Claude testing without trying what is arguably their most established security feature. The /security-review command is designed to review pending changes in the current branch, but with a bit of Git magic we can make the entire repository appear as pending changes. Close enough.

The results are as follows:

The only finding that made it through is the SHA-1 password hashing. No trace of the business logic bugs we uncovered earlier.

Summary:

Quite a disappointing result, especially given that /security-review is probably used a lot by developers who want an accessible AI-assisted security audit without having to think about prompts at all. It did not just fail to find anything new, it actually fell short of what raw Claude Code had already caught with a single generic prompt.

Claude Code Security

We did not manage to get access to Claude Code Security.

OpenAI

Codex

Of course, we also had to put Codex through the same experiment that started this whole journey: a single prompt, asking it to "perform a comprehensive security audit".

Compared to Claude, Codex is noticeably more restrained with its findings, but also more systematic in its approach, starting by mapping out the security boundaries, endpoints, and trust model before diving in.

It came back with 3 findings: 1 high and 2 mediums.
The high is the UserPermGuestUploads bypass via an API key with ApiPermManageFileRequests, which we already discussed in the Anthropic section.

The two mediums are ZIP downloads preserving unsafe filenames and allowing archive path traversal (not a vulnerability in Gokapi's context), and the familiar missing security cookie flags.

Summary:

On the bright side, fewer false positives than Claude, but unfortunately fewer findings overall too. Not quite what you would hope for when asking for a "comprehensive security audit".

Codex Security

While we did not manage to get our hands on Claude Code Security, we did get access to its OpenAI counterpart. Codex Security is OpenAI's application security agent. Fresh out of research preview and already generating buzz, it felt like a natural candidate to explore. Codex Security officially only supports scanning commits from the past two months, but with a few Git tricks we can make it scan the full Gokapi repository.

The result surprised us. Only a single finding: the Content-Disposition header injection we already flagged in the Claude section, where file.Name is potentially attacker-controlled. As we established earlier, CRLF injection is not actually possible here thanks to Go's header validation. So we are looking at one finding, and it has limited impact.

We thought the first result was a fluke, so we ran it again. This time things looked very different:

45 findings. That is a lot to unpack. Let's see how many of them actually hold up.

The first high targets the OpenID Connect SSO integration. Gokapi lets you restrict access to specific OIDC "Authorised groups", with wildcard support:

The function that checks a user's group against the configured allowlist looks like this:

func matchesWithWildcard(pattern, input string) (bool, error) {
    components := strings.Split(pattern, "*")
    if len(components) == 1 {
        // if len is 1, there are no *'s, return exact match pattern
        return regexp.MatchString("^"+pattern+"$", input)
    }
    var result strings.Builder
    for i, literal := range components {
        // Replace * with .*
        if i > 0 {
            result.WriteString(".*")
        }
        // Quote any regular expression meta characters in the
        // literal text.
        result.WriteString(regexp.QuoteMeta(literal))
    }
    return regexp.MatchString("^"+result.String()+"$", input)
}
Enter fullscreen mode Exit fullscreen mode

Noticed the bug? The default branch (when wildcards are present) correctly calls regexp.QuoteMeta on the input, but the early exit branch (no wildcards) skips it entirely. This means if the configured allowed group is john.doe, someone with the group johnXdoe will sail right through the allowlist. A genuine vulnerability, though we would call it a medium in practice given that an attacker would need some degree of control over SSO identities to exploit it (the Gokapi's maintainer reached a similar conclusion and decided not to issue a CVE for it).

The second high impact vulnerability, "External StreamSaver MITM", flags a situation where Gokapi is running over plain HTTP. Since service workers cannot be registered over insecure connections, the app falls back to a proxy hosted on the creator's GitHub Pages. The man-in-the-middle potential is real, but this looks like a deliberate tradeoff: the service worker exists specifically to avoid buffering large encrypted files in memory (and frankly, if you are running a production file service over HTTP you have bigger problems - why don't just MITM the connection itself?).

The other two highs are unauthenticated setup and header-based auth, both of which we have already seen and both of which are intentional behaviours.

Moving on to the 8 mediums, we were able to confirm 2 as valid, and they both hit the same feature: hotlinking. You cannot create hotlinks for HTML pages, but what if you create a hotlink for a PNG and then swap it out for an HTML file? The check gets bypassed. The same goes for SVGs. Both result in a stored XSS reachable by any authenticated user. Nice finding.

The remaining mediums fall into the "weakness or false positive" bucket given Gokapi's threat model.

Most of the lows follow the same pattern, with a few exceptions worth calling out: the "Cleanup deletes all files" bug we already know well by now, the SVG hotlink XSS surfacing again, and a log injection vector via username.

So out of 45 findings, only a handful have real, direct impact. Notably, the business logic bug around UserPermGuestUploads vs ApiPermManageFileRequests, which Codex itself found in the previous run, is nowhere to be seen this time around. Even the rest of the findings are worth knowing about and fixing, but they do not pose immediate risk.

Summary:

The signal to noise ratio is rough, but the signal itself is worth noting. The stored XSS that every other tool had missed up to this point is a genuinely good find, and the SSO bug is a nice catch too. Less impressive is the tendency to report the same vulnerability multiple times from different angles rather than consolidating findings around a root cause, which makes the report harder to navigate than it needs to be.

It also has to be said that Codex Security is still clearly early: the UI has quite a few rough UI/UX bugs that make the experience more painful than it should be.

AISafe Labs

Thus far we have explored individual LLMs and how well they perform when given increasingly richer context and more detailed instructions. From a single generic prompt, to bug-class specific ones, to a full suite of infosec skills bolted on top. Now we have reached the final boss, how much extra value will the custom orchestration, knowledge base and tools built into AISafe bring?


Preview of AISafe app

The audit returned 2 high impact vulnerabilities.

The first one is titled "Data Leak in Upload Status Stream". Gokapi uses HTTP server-sent events to stream upload progress to the user's browser. The problem is that authorization on this endpoint is completely broken: any authenticated user receives events about every file upload happening across the entire application. That includes chunk IDs, which could allow an attacker to interfere with another user's active upload, and file IDs, which would let them download files they were never meant to see. This is a serious finding, and it is genuinely surprising that none of the other tools in this experiment spotted it.

The second high is the unauthenticated setup wizard, which as we have established is an accepted risk for Gokapi.

14 mediums. Let's go through them, because there is a mix of familiar faces and some interesting new ones.

Two we have already seen: the UserPermGuestUploads vs ApiPermManageFileRequests privilege escalation, and the stored XSS in hotlinks.

The first novel finding is a nice catch around API key demotion. When a user gets demoted, previously issued API keys do not automatically lose the privileges they should. In practice, a stale key can retain ApiPermManageFileRequests and keep managing upload requests long after the account behind it no longer has that capability.

The second is a privilege escalation in the file replacement feature. Cross-user replacement is guarded too loosely: if you have permission to list other users' uploads, you can point idNewContent at someone else's file during a replacement and, with deleteNewFile=true, have the storage layer silently delete it without any stronger authorization check. The victim's file is gone.

The third new finding targets chunk uploads. The size of the current HTTP request body is checked, but the total declared file size is taken from attacker-controlled metadata and then used during allocation and completion. By splitting a file into chunks and lying about the right fields in the metadata, an attacker can smuggle a file that exceeds the configured upload limit. A clean quota bypass.

That is 2 familiar findings and 3 new ones. What about the remaining 9?

Two are CSRF reports, one for the login page and one for the password reset page, but this time with actual impact attached. AISafe noticed, that the password reset functionality uses r.Form.Get("newpw"), which accepts both POST and GET parameters with no method check, effectively bypassing the SameSite=Lax default protection that would otherwise limit CSRF exposure.

Two more are denial of service findings: one in the E2E Metadata Parser where the content parameter is stored in memory twice, creating an amplification factor; and one in the Upload Status SSE Endpoint, which we already know but have determined has no meaningful real-world impact.

The remaining five are a mixed bag:

  • SSRF in the setup page: accepted risk
  • Authorization "bypass" via file add API: determined by the maintainer to have no security implications
  • Race condition in chunk file upload: theoretical due to a very narrow race window
  • Header spoofing in header auth mode: already discussed, intentional behaviour
  • Sessions not invalidated after password change: a valid weakness worth fixing

The remaining 4 lows are mostly theoretical or intentional behaviour.

Summary

AISafe was able to identify the SSE broadcast vulnerability, 1 DoS, and 3 unique logic bugs that resulted in CVEs, that the other tools haven’t discovered. Why? Well, looks like custom orchestration, knowledge base, guided threat modeling, and other secret sauces making up AISafe have big effect.

On the other side, the valid findings that other tools caught and AISafe did not, such as the file list vs. replace permission mismatch, the OIDC bug, and the file request cleanup typo, and some of the low severity findings. are worth putting in context: only the first and second have some security impact, even with the second one having serious preconditions necessary for exploitation. The low impact findings are better described as security improvements than actual vulnerabilities.

Summary

Product Findings Vulnerabilities with direct impact Weaknesseses without direct impact Accepted risk False positives
Claude Code 24 1 8 11 4
Claude Code w Skills 21 0 5 2 14
Claude Code /security-review 1 0 1 0 0
Codex 4 2 2 0 0
Codex Security 44 6 18 8 11
AISafe 20 9 4 7 0

Conclusion

So, back to our initial question: can a single prompt catch all the vulnerabilities in your application? Based on what we have seen today, no, and the gap between a raw LLM and a purpose-built security tool is not just about how many tokens you throw at the problem, but it comes down to how you spend those tokens. Flooding a report with low-impact findings and suggestions is easy; finding the complex logic issues is not.

At the end of the day, the results will always tell their own story. Security is not something you can afford to overlook. Fixing 45 reported issues might feel like a great achievement, but quality over quantity. That's why choosing the right tool matters just as much as choosing to act in the first place. And we hope that this case study has given you a clearer picture of where things stand today.

Quality over quantity is not just a nice principle in security research, it is the whole point why we strive to make quality accessible to everyone!

Check out our Code Audit service or get in touch directly.

CVEs & advisories

We'd also like to thank Gokapi's maintainer Forceu for a great collaboration on resolving and classifying the issues!

7 CVEs were assigned in total for the issues found by AISafe:

Top comments (0)