Open source software (OSS) poses a uniquely serious supply chain threat, making it a prime target for attackers and a productive target for Bug Bounty hunters.
Some 97% of codebases contain OSS components, while a vulnerability in a single library can affect thousands of downstream applications and organisations.
So researchers who perform open source analysis are very much in the frontline of the global cyber war. Any dependency that organisations rely on could be hiding the next Log4Shell, the infamous vulnerability affecting hundreds of millions of systems around the world.
Moreover, with open-source programs increasingly backed by private companies and governments – including the European Commission and, indirectly, the German government on YesWeHack – Bug Bounty rewards are perhaps higher than some might assume.
And with source code available, testing is more targeted than black-box approaches: researchers can trace data flows, identify vulnerable patterns and build exploits with precision.
This guide shows you how.
What is open source?
Open source refers to software whose source code is publicly available for anyone to view, use, modify and distribute. This openness allows developers around the world to improve software collaboratively through feature suggestions, code contributions and pull requests.
Open source software (OSS) also plays a vital role in enhancing security and transparency. Because the code is visible to everyone, it’s easier for developers and security researchers to identify and fix vulnerabilities. Rather like crowdsourced security testing, this collective effort makes open source software not only more innovative but also more trustworthy and resilient over time.
Picking open source Bug Bounty Programs
Choosing an open source Bug Bounty Program should be partly guided by your personal interests. Working on technologies or domains that genuinely interest you makes it easier to stay motivated while reading source code and hunting for vulnerabilities.
You should also consider several practical factors, such as:
- Programming language and ecosystem – some languages offer stronger tooling, clearer documentation and well-understood vulnerability classes, which can make security auditing more effective
- Application type and attack surface – consider whether the target is a web application, mobile app, kernel driver, CLI tool, framework or library, as each exposes different entry points and testing approaches
- Security impact and vulnerability potential – evaluate whether the project’s functionality could lead to high-impact vulnerabilities such as remote code execution, authentication bypass or privilege escalation.
Start browsing public open source programs on YesWeHack.
Any vulnerabilities you discover will appear on a feed of open source bugs, which sits alongside our open-source leaderboard.
Effective open source code analysis techniques
Code analysis tools help hunters efficiently scan, track and understand open-source code, making it easier to identify potential vulnerabilities and security issues. Broadly speaking, code analysis can be divided into static and dynamic approaches, each with its own strengths, limitations and use cases.
Static application security testing (SAST)
Static Application Security Testing (SAST) is used to identify security issues by analysing an application’s source code, bytecode or binary without executing the program. The technique scans code for insecure patterns, unsafe coding practices and potential vulnerabilities using methods such as data flow analysis, control flow analysis and abstract syntax tree parsing.
Advantages of SAST
- Scales well and can analyse large codebases quickly
- Detects common vulnerable code patterns early in the development lifecycle
- Helps build a core understanding of code structure that downstream tools and analyses can leverage
Limitations of SAST
- Limited ability to detect dynamic behavior at runtime
- Cannot detect business logic flaws or runtime problems like race conditions
- Can generate high false positive rates that require manual validation
Dynamic application security testing (DAST)
Dynamic Application Security Testing (DAST) identifies security issues by analysing an application while it is running. Instead of inspecting source code, DAST tests the application externally by sending crafted inputs and observing outputs. This approach helps detect vulnerabilities that occur at runtime, such as injection flaws, authentication weaknesses and misconfigurations.
Advantages of DAST
- Simulates real-world attacks by interacting with the application’s live environment and adapting to its behaviour and logic changes
- Excels at uncovering vulnerabilities that only appear when the application is running
- Minimises false positives by focusing on actual, exploitable weaknesses rather than theoretical risks
Limitations of DAST
- Managing complex workflows and dependencies can make testing cumbersome in large codebases
- Identifies what is vulnerable but provides little detail about where in the source code the issue originates.
- Only analyzes code executed at runtime, leaving unreachable or untriggered paths untested
Taint analysis
Taint analysis tracks how untrusted external input flows through a program. Data from outside sources – such as user input or API responses – is marked as ‘tainted’ and the analysis follows its path through the codebase.
If tainted data reaches a sensitive operation, such as a system command or database query without first being sanitizing, it flags a potential vulnerability.
Advantages of taint analysis
- Focuses on vulnerabilities where untrusted input can influence reach security-sensitive operations
- Helps guide fuzzers and other testing tools toward valuable sinks and potential exploitation paths
Limitations of taint analysis
- Can generate false positives because it considers many possible execution paths, not all of which are truly exploitable
- Requires language- and framework-specific configuration to correctly identify sources, sanitisation routines and sinks
Control-flow graph (CFG)
A control-flow graph (CFG) maps all possible execution paths through a program. Each node in the graph represents a block of code, making it easier to track how execution moves from one block to another. Security researchers use CFGs to understand program behaviour, spot dead or unreachable code, and find unusual or potentially dangerous logic flows.
Advantages of CFG
- Provide a visual overview of code execution flow, aiding comprehension
- Makes unreachable code easier to identify
- Serves as a foundation for advanced analysis techniques such as taint analysis, symbolic execution, and program slicing
Limitations of CFG
- Can become difficult to interpret in large codebases with many complex execution paths
- Does not capture how data flows through the program, only how execution moves between code blocks
Fuzzing
Fuzzing is a dynamic testing technique that repeatedly feeds programs with malformed, unexpected or random inputs, with the goal of triggering crashes or abnormal behaviour that reveals hidden vulnerabilities. Modern fuzzers learn from how the program reacts and use that feedback to create even better test cases. This means fuzzing is an effective way to uncover real-world vulnerabilities that static or manual analysis might miss.
Advantages of fuzzing
- High execution speed allows unexpected behaviour to be identified rapidly
- Can run continuously for extended periods with minimal manual intervention
- Often discovers issues that other tools fail to detect
Limitations of fuzzing
- Requires careful configuration and can be complex to integrate into large or advanced codebases
- Crashes can be difficult to analyse, and identifying the root cause may require significant manual investigation
- Limited visibility into code paths that require complex state, authentication, or specific input sequences
Code analysis tools and resources
Open source scopes give you a head start insofar as you have access to your target’s source code, but you still need a number of tools to make sense of it. Below are some recommended tools for handling the ‘heavy lifting’ for tasks such as scanning for vulnerable code patterns, performing taint analysis, or simply organising the source code to make it easier to understand.
RELATED The top five hacking tools for white-box pentesting
Recommended integrated development environment (IDE): Visual Studio Code (VS Code)
An IDE is the foundation of source code analysis and Visual Studio Code (VS Code) is a popular choice, in part because it is itself open source and supports an extensive library of community-built extensions. To give you a sense of its popularity, VS Code is to analysing and debugging source code what Burp Suite and Caido are to dynamic application testing.
Recommended language-specific fuzzers
It is recommended to use a fuzzer tailored to the programming language of your target. Language-specific fuzzers are designed to follow the language's runtime execution model, resulting in more accurate code coverage and higher-quality test cases.
- American Fuzzy Lop plus plus (AFL++): targets natively compiled binaries such as C, Golang and Rust
- Jazzer: Targets Java bytecode running on the JVM
- Atheris: Targets Python bytecode (.pyc) or JIT-compiled Python via PyPy
- Fuzzilli: Targets JavaScript engines, covering both interpreted and JIT-compiled execution paths
Recommended proxy attack tools for web and mobile applications: Burp Suite and Caido
If your target is a web application, a proxy tool will be essential for validating vulnerabilities identified through source code analysis. For example, if you’re assessing an open-source Bug Bounty target such as OWNCloud – a web-based file sharing solution – you’ll need a tool capable of intercepting and modifying HTTP traffic.
Rather than using Burp Suite or Caido for their automated scanning, crawling, or fuzzing features, the focus here is different: crafting precise HTTP requests to test specific theories and execute targeted attacks based on possible vulnerabilities you already identified in the source code.
GitLens as a VS Code extension: View git history
GitLens is a powerful VS Code extension that allows you to explore a project’s Git history in detail. With GitLens, you can view commits, track issues and see which developer made specific changes.
This capability is valuable for code analysis because it helps you understand how the code has evolved over time, identify the introduction of potential vulnerabilities, and verify whether previous security issues have been properly fixed. By connecting the code changes to specific developers and commits, GitLens also makes it easier to conduct thorough audits and trace the origin of complex logic in the application.
Snyk as a VS Code extension: Detect vulnerable code dependencies
Snyk is a multi-layered static analysis and dependency scanner that allows you to analyse source code, open-source dependencies and Infrastructure as Code (IaC) configurations.
It works by continuously analysing your code and providing real-time feedback about:
- Vulnerable dependencies – flagged against Snyk's continuously updated vulnerability database
- Insecure code patterns – identified through static analysis (SAST)
- Infrastructure misconfigurations – covering Terraform, Kubernetes and CloudFormation files
- Container vulnerabilities – detected within container image definitions
Snyk can be run as a standalone CLI tool or integrated directly into VS Code using the official Snyk extension. Once Snyk is configured inside VS Code, you can start scanning your source code for vulnerabilities.
Detecting vulnerabilities in source code with Synk
Let’s put Synk to the test by performing a code analysis scan against the OWASP Juice Shop source code. To run a scan, we open VS Code’s command palette with CTRL + SHIFT + P and search for Snyk: Rescan. Results appear in Snyk's extension panel on the left sidebar once the scan completes.
As you might have expected, the scan surfaced a ton of vulnerabilities in OWASP Juice shop. Clicking any finding navigates directly to the relevant source code, where Snyk displays the full data flow and a description of the vulnerability.
One finding of note was an SSRF vulnerability in the profileImageUrlUpload function. The data flow is as follows:
- User-controlled input is taken from the HTTP POST body parameter
imageUrl. Code: if (req.body.imageUrl !== undefined) - The value of
imageUrlis assigned to the variableurl. Code:const url = req.body.imageUrl - The variable is passed directly to a fetch call without validation. Code:
const response = await fetch(url)
To confirm the vulnerability, we will run OWASP Juice shop in a docker:
1docker pull bkimminich/juice-shop2docker run --rm -p 127.0.0.1:1337:3000 bkimminich/juice-shop
Once running, the SSRF can be validated by sending the following raw HTTP request:
1POST /profile/image/url HTTP/1.12Host: localhost:13373Content-Type: application/x-www-form-urlencoded4Cookie: language=en; welcomebanner_status=dismiss; token=<your_session>5Content-Length: 7067imageUrl=http://127.0.0.1:3000/assets/public/images/JuiceShop_Logo.png
The result: our profile picture successfully changed to OWASP Juice shop’s logo, which we fetched from localhost – confirming the SSRF’s validity.
Detecting vulnerable dependencies with Synk
Snyk maintains a large database of known vulnerabilities in open source packages, including CVEs and flagged malicious libraries. In the same way it analyses your own source code, Snyk scans all project dependencies and produces a detailed report of any identified issues.
By recording these results, we can quickly identify whether our target relies on outdated or vulnerable dependencies.
CodeQL: Write advanced queries for taint and SAST analysis
CodeQL works by ingesting source code and building a structured database that can be queried using its own query language, QL. It enables robust static application security testing (SAST) and taint analysis, helping security researchers identify potential vulnerabilities and trace unsafe data flows across a codebase. By representing code as a queryable database – similar in concept to SQL – CodeQL makes it possible to search for security issues and coding patterns at scale.
I won’t cover the setup process here as this is outlined clearly in the official CodeQL documentation. Instead, this section focuses on how CodeQL can be used to enhance source code security analysis.
What are CodeQL queries?
CodeQL queries are the primary mechanism for analysing source code in CodeQL. They are written in the CodeQL query language (QL), a declarative logic programming language designed to represent code as data.
A CodeQL query specifies what you want to find in a codebase rather than how to find it. Queries can range from simple searches, like listing all functions or classes, to complex analyses that trace data flow, detect security vulnerabilities or enforce coding standards. A common use case is following user-controllable input through the codebase to identify the sink it reaches, making vulnerability discovery more precise and systematic.
CodeQL's database is a semantic representation of the code, capturing its syntax, control flow, type information and data flow relationships. This allows queries to detect patterns such as:
- Unsafe handling of user input (taint analysis)
- Misconfigurations and code smells
- Improper sanitisation resulting in exploitable vulnerabilities
Using CodeQL queries to detect vulnerabilities
You can use the official CodeQL query packs from GitHub or write your own. The official packs contain a wide range of checks spanning general code quality, misconfiguration detection and security issues mapped to CWEs.
To write custom queries, refer to the CodeQL documentation. As a simple example, the following QL query lists all function names in the target codebase:
1/** * This is an automatically generated file * @name Get function names * @kind info * @problem.severity info * @id javascript/example/getfunctions */23import javascript45from Function f6select f.getName()
When run against OWASP Juice Shop, the results will resemble the following:
Example: SQL injection detection
Because CodeQL models types, control flow, and data flow, you can write queries that detect dataflow-based vulnerabilities such as SQL injection. The query below uses CodeQL’s built-in JavaScript SQL injection flow model to identify exploitable paths:
1/** * @name Database query built from user-controlled sources (SQL only) * @description Building a database query from user-controlled sources is vulnerable to insertion of * malicious code by the user. This query only checks for SQL injection flows. * @kind path-problem * @problem.severity error * @security-severity 8.8 * @precision high * @id js/sql-injection * @tags security * external/cwe/cwe-089 */23import javascript4import semmle.javascript.security.dataflow.SqlInjectionQuery as Sql56from Sql::SqlInjectionFlow::PathNode source, Sql::SqlInjectionFlow::PathNode sink7where8 Sql::SqlInjectionFlow::flowPath(source, sink)9select sink.getNode(), source, sink,10 "This SQL query depends on a user-provided string.",11 source.getNode(), "user-provided value"
Analysing the results, we can see that a SQL injection was detected in the OWASP Juice shop codebase:
This illustrates how CodeQL can significantly improve source code analysis – making it easier to trace how user input is handled and identify the sinks it ends up in.
Navigating and annotating code with TODO Tree & Bookmarks extensions
Besides security-focused tools, we also need ways to organise and track progress through a codebase. You can combine the VS Code extensions TODO Tree and Bookmarks to achieve this.
With Bookmarks, you can navigate to specific lines of code, right-click and add a bookmark with a label. This makes it easy to return to points of interest or mark lines that may contain potential vulnerabilities.
Todo Tree can also be used to track important code locations, by navigating to the code you want to mark and adding a comment containing a supported tag, such as:
1// TODO: validate filter for x function...
A particularly useful feature is the ability to define custom tags. For example, you could use a custom tag like VULN instead of generic markers such as TODO, making it easier to track security-relevant notes throughout your code.
Code analysis methodology for finding vulnerabilities
In this section, we demonstrate a structured methodology for performing code analysis to detect vulnerabilities. Using OWASP Juice Shop as our target, we will start from scratch and walk through each step of the process.
Setting up the environment
Before we can start our code analysis, we need to set up our tools and target environment:
Run the OWASP Juice Shop source code locally in Docker (Note: NODE_ENV=unsafe enables all challenges - ensure the container is bound to 127.0.0.1):
1docker run -it -e "NODE_ENV=unsafe" -p 127.0.0.1:1337:3000 bkimminich/juice-shop
Create a CodeQL database from the OWASP Juice Shop source code, replacing /path/to/juice-shop with the correct path to the source:
1codeql database create juice-shop \2--threads=10 \3--language=javascript \4--command="npm i" \5--source-root="/path/to/juice-shop";
Open OWASP Juice Shop’s source code in VS Code:
1cd /path/to/juice-shop && code .
Create a new project in your proxy tool of choice (Burp Suite, Caido, ZAP, etc):
Performing code analysis against OWASP Juice shop
With the environment in place, we can begin analysing OWASP Juice Shop. The following walk-through illustrates a realistic scenario for applying our methodology to identify vulnerabilities in the source code and validate them using a proxy attack tool.
Because Juice Shop is intentionally vulnerable, tools like Snyk will immediately report numerous issues. We could investigate and exploit all of those findings (as we would on a real application), but for this exercise we’ll go deeper: using CodeQL to write custom queries that reveal how user-controlled data flows through the application, then validating what we discover through our attack proxy.
Creating our first CodeQL query for discovering user-controllable data:
1/** * @name Taint remote sources * @description Sources of untrusted user input. * @kind problem * @problem.severity info * @id javascript/example/taint-remote-sources * @tags summary * @precision medium */23import javascript4import semmle.javascript.dataflow.TaintTracking56from RemoteFlowSource node7select node, node.getSourceType()
Our first query uses TaintTracking (semmle.javascript.dataflow.TaintTracking) to identify all remote flow sources – that is, input entering the application from an external user. Using RemoteFlowSource and the getSourceType function, the query returns every point in the codebase from where attacker-controllable data originates.
We now understand what we can control in the application. From here, we can dig deeper and examine how those controllable variables are used.
Identifying a Zip Slip vulnerability
Reviewing the results, we found that the file parameter in the handleZipFileUpload function is attacker‑controllable. Further examination reveals a Zip Slip vulnerability: the function extracts a ZIP archive and writes its contents to disk without sanitising the filenames inside the archive. An attacker can embed ../ (dot‑dot‑slash) sequences in filenames to perform path traversal and overwrite arbitrary system files.
To trace how this function is used, we can find all references in VS Code by right-clicking the function name and selecting ‘Find All References’ (SHIFT + ALT + F12). This shows that the function is called in server.ts, mapped to the web route /file-upload:
A subsequent global search (CTRL+SHIFT+F) confirms that this endpoint is also referenced within an Angular component:
Verifying and exploiting a Zip Slip vulnerability
With a clear understanding of the vulnerability, we can now validate it in practice. The upload feature is accessible at http://localhost:1337/#/complain.
Before uploading anything, we need to craft a malicious ZIP file. For this we use Archive Alchemist, a tool developed by avlidienbrunn designed for exactly this purpose.
To identify the correct system path on the server, we perform a global search in VS Code for ftp/.
The system path for the FTP directory can be spotted directly in the Dockerfile:
We then generate our malicious ZIP using the following command:
1archive-alchemist zipslip.zip add "../../../../../../juice-shop/ftp/pwned.txt" --content "pwned!"
This produces a zip file containing the filename ../../../../../../juice-shop/ftp/pwned.txt with the content pwned!. If the exploit succeeds, this file should be written to the FTP directory on the server.
We then type ‘zipslip’ into the required message field of the complaint form before uploading our ZIP file.
To confirm successful exploitation, we navigate to http://localhost:1337/ftp – if the vulnerability is present, our file will appear in the directory listing.
The presence of pwned.txt in the listing confirms that the application is indeed vulnerable to Zip Slip and that the vulnerability is fully exploitable.
Responsible disclosure: how to report open-source bugs
How you disclose a vulnerability depends on the designated disclosure channels.
For projects without a formal Bug Bounty Program, contact the maintainers directly – look for a SECURITY.md file in the repository or a security.txt file listing a preferred security contact. For comprehensive guidance on the disclosure process, the OWASP Vulnerability Disclosure Cheat Sheet is a practical reference.
If you’re pondering your next target, then check out the open source scopes available on YesWeHack.
RELATED European Commission’s latest Bug Bounty tender won by YesWeHack
Once you’ve chosen a program, understood the program rules, then discovered and validated a vulnerability, it’s time to click ‘SUBMIT REPORT’. Walk through the vulnerability step by step, covering how it was discovered, the vulnerability type, the affected code, and a clear proof of concept. Finally, describe the impact and offer recommendations for mitigation, which helps the security team understand the severity and remediate efficiently.
The more detailed and precise your report, the better your chances of a higher reward, an improved leaderboard position – including on our open source-specific leaderboard – and receiving invitations to private programs.
SHARPEN YOUR DISCLOSURE SKILLS How to write an effective Bug Bounty report
Conclusion: Putting your code-analysis skills into practice – responsibly
Open-source software powers the modern internet. Discovering vulnerabilities in publicly available code can have far-reaching impact, helping protect millions of users who rely on the software directly or indirectly.
The techniques covered in this guide – such as source code analysis, dependency scanning with Snyk and advanced queries with CodeQL – can help uncover exploitable flaws in libraries that are reused across thousands of downstream applications.
Whatever your target (open source or proprietary), make sure you test within safe, controlled environments. Document your findings carefully and submit reports that clearly explain the vulnerability, demonstrate it with a reliable proof of concept, and clearly describe its potential impact.
Because responsible disclosure isn’t just good ethics – it’s what turns technical skills into meaningful, real-world impact. By hacking ethically, adhering to program rules and collaborating with maintainers, you help make open source safer and more resilient for everyone.
Stay curious. Keep testing. And keep hacking for good.
References
- Visual Studio Code (VS Code)
- OWASP Juice Shop
- CodeQL
- CodeQL extension for Visual Studio Code
- Visual Studio Code extension on Synk User Docs
- Debug code with Visual Studio Code – documentation on VS Code
- YesWeHack authorised as a CVE Numbering Authority (CNA) – article on YesWeHack
- Open source public Bug Bounty Programs on YesWeHack



