Webinar: Let's make a programming language. Lexer - 29.04
This article covers changes in the OWASP Top 10 for 2025 with examples and breaks down how SAST can help avoid vulnerabilities.

The OWASP Top 10 is a list of ten most critical security risks to web applications. It serves as a guide for developers, architects, and information security professionals, helping them focus on the most severe risks when building web applications.
Since the previous list version was released in 2021, the threat landscape has changed significantly. In 2025, OWASP presented its updated edition: OWASP Top 10:2025.
This article tells about the new vulnerability categories in the updated list and demonstrates how to detect some of these threats using PVS-Studio static analyzer. For OWASP, there is a separate category of diagnostic rules. You can read more about it in the documentation.
We have already written about the previous version of OWASP Top 10 (2021)—you are welcome to read the article via this link.
As mentioned earlier, the OWASP Top 10 is updated not only for the sake of novelty—changes to the list reflect the evolution of real-world threats. Compared to the 2021 version, the new list retains most categories but revises their priorities and introduces two new ones: A03:2025 Software Supply Chain Failures and A10:2025 Mishandling of Exceptional Conditions.
Besides, some descriptions have been refined to better align with modern practices. For example, the emphasis in logging has shifted from passive event recording to the need for proactive alerting and response, and the focus on authentication has become more precise.
This category includes vulnerabilities that lead to unauthorized information disclosure, modification, or destruction of all data, or the execution of business functions outside the user's intended privileges.
In OWASP Top 10:2025, this category additionally includes risks previously listed separately as Server-Side Request Forgery (SSRF)—in the previous version (2021), SSRF has taken the A10 position. This consolidation emphasizes the common nature of these threats—uncontrolled access to resources: internal files, network services, or other users' data.
Broken Access Control remains in first place, confirming that access control errors are among the most critical and widespread problems in modern web applications.
Look at this code fragment from the Power-Fx project:
public static void Check(Engine engine, string pathInput)
{
EngineSchema schema;
if (pathInput != null)
{
var json = File.ReadAllText(pathInput);
schema = JsonSerializer.Deserialize<EngineSchema>(json);
}
....
}
The PVS-Studio warning: V5609 Possible path traversal vulnerability. Potentially tainted data from the 'pathInput' variable is used as path.
Here, the file path comes directly from a user via the pathInput parameter, is only checked for null and is immediately used to read the file. This is a classic path traversal vulnerability: an attacker could pass a value like ../../../etc/passwd and gain unauthorized access to any files on the server.
Broken Access Control problems aren't limited to the file system. They also occur, for instance, at the authorization logic level.
In Spring, the vote method from AccessDecisionVoter can be implemented as follows:
@Override
public int vote(Authentication authentication,
FilterInvocation filterInvocation,
Collection<ConfigAttribute> attributes) {
boolean isAdmin = hasAdminRole(authentication);
String requestMethod = filterInvocation.getRequest().getMethod();
if ("DELETE".equals(requestMethod) && !isAdmin) {
return ACCESS_GRANTED;
}
return ACCESS_GRANTED;
}
Here, the method always returns ACCESS_GRANTED, including cases where a regular user attempts to perform a privileged operation. This is a direct violation of the least privilege principle.
For such an error, PVS-Studio analyzer would trigger diagnostic rule V5328: Using non-restrictive authorization checks could lead to security violations.
This category combines vulnerabilities related to insecure configuration: enabling unnecessary ports or services, insecure configuration, using external data to define system properties, etc.
Since the previous version, this category has risen from 5th to 2nd place in the list. This signals that even minor configuration errors are increasingly becoming an entry point for attacks on applications.
For example, a configuration problem may occur due to external data.
public void ExecuteSqlQuery(....)
{
....
string catalog = Request.QueryString["catalog"];
using (SqlConnection dbConnection = IO.GetDBConnection())
{
dbConnection.ConnectionString = $"Data Source=....; " +
$"Initial Catalog={catalog}; " +
$"User ID=....; " +
$"Password=....;";
....
}
....
}
In this example, a connection string for the database is being formed. Data that hasn't undergone any prior validation is written into the Initial Catalog parameter, allowing an attacker to pass any catalog name and obtain data they shouldn't have access to.
We could fix this by checking the user-provided data:
public void ExecuteSqlQuery(...., HashSet<string> validCatalogNames)
{
....
string catalog = Request.QueryString["catalog"];
if(!validCatalogNames.Contains(catalog))
return;
using(SqlConnection dbConnection = IO.GetDBConnection())
{
dbConnection.ConnectionString = $"Data Source=....; " +
$"Initial Catalog={catalog}; " +
$"User ID=....; " +
$"Password=....;";
....
}
....
}
Here, before using the obtained value, we check that catalog exists in the validCatalogNames collection. This allows the user access only to a limited set of catalogs.
PVS-Studio analyzer detects such defects using the diagnostic rule V5624: Use of potentially tainted data in configuration may lead to security issues.
Configuration problems can also lead to the risk of an XXE attack. For instance, an XML parser receiving data from an external source can be configured insecurely.
Let's take this XML file format as an example, which the application is supposed to work with:
<?xml version="1.0" encoding="utf-8" ?>
<shop>
<itemID>62</itemID>
</shop>
Let's suppose the following code handles the processing:
public static void processItemWithID(String pathToXmlFile) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
var document = builder.parse(pathToXmlFile); // <=
var nodeList = document.getElementsByTagName("itemID");
String itemiD = nodeList.item(0).getTextContent();
try {
long itemIDvalue = Long.parseLong(itemiD);
// process the item with 'itemIDvalue' value
System.out.printf("An item with the %d ID was processed.%n", itemIDvalue);
} catch (NumberFormatException e) {
System.out.printf("%s is not valid 'itemID' value.%n", itemiD);
}
}
For the XML file above, the application will output this line:
An item with the '62' ID was processed.
If we try to use an invalid value for the ID field, the application reports an error:
"Hello world" is not valid 'itemID' value.
Although the code performs its assigned task, it is vulnerable to XXE attacks for several reasons.
To compromise this code, an attacker could use, for example, the following XML file:
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file://D:/MySecrets.txt">
]>
<shop>
<itemID>&xxe;</itemID>
</shop>
This file declares an external entity, xxe, which the parser will process. As a result, the contents of the D:/MySecrets.txt file (for example, This is an XXE attack target), located on the machine running the application, will output to the user:
This is an XXE attack target. is not valid 'itemID' value.
To protect against such an attack, you can disable external entity processing and ignore DTD processing. For the example above, we would do as follows:
String feature = "http://apache.org/xml/features/disallow-doctype-decl";
factory.setFeature(feature, true);
PVS-Studio analyzer detects such security defects using the diagnostic rule V5335: Potential XXE vulnerability. Insecure XML parser is used to process potentially tainted data.
In OWASP Top 10 2025, the former category Vulnerable and Outdated Components (A06:2021) has evolved into A03:2025 – Software Supply Chain Failures. This change emphasizes that threats are no longer limited to using outdated libraries: today, attackers target the entire supply chain—from code repositories and CI/CD pipelines to final deployment artifacts.
However, the list of CWEs for this category still consists almost entirely of problems with used components:
PVS-Studio can find vulnerable components in C# projects using rule V5625: Referenced package contains vulnerability. It uses the SCA mechanism and checks that the dependencies used in the project have no known vulnerabilities.
This category combines a group of vulnerabilities related to incorrect encryption of confidential data: using outdated cryptographic algorithms or hash functions, passing unencrypted data, etc.
Compared to the previous version, this category has dropped from second to fourth position.
It includes errors related to using outdated encryption or hashing algorithms. For example, our tool has rule V5314: Use of an outdated hash algorithm is not recommended.
Here is an example of this rule triggering in the DBeaver project:
private boolean checkLockPassword() {
BaseAuthDialog dialog = new BaseAuthDialog(....);
if (dialog.open() == IDialogConstants.OK_ID) {
final String userPassword = dialog.getUserPassword();
if (!CommonUtils.isEmpty(userPassword)) {
try {
final byte[]
md5hash = MessageDigest.getInstance("MD5") // <=
.digest(userPassword.getBytes(....));
final String hexString = CommonUtils.toHexString(md5hash)
.toLowerCase(Locale.ENGLISH)
.trim();
if (hexString.equals(dataSource.getLockPasswordHash())) {
return true;
}
UIUtils.showMessageBox(....);
} catch (Throwable e) {
DBWorkbench.getPlatformUI().showError(....);
}
}
}
return false;
}
The PVS-Studio warning V5314. Use of the 'MD5' hash algorithm is not recommended. Such code may cause the exposure of sensitive data.
Here, the analyzer indicates that the MD5 encryption algorithm is outdated.
But there are other suspicious cases related to cryptography that can lead to application vulnerability. For example, as in this fragment from the same DBeaver:
public static String generateNewId(DBPDriver driver) {
long rnd = new Random().nextLong(); // <=
if (rnd < 0) rnd = -rnd;
return driver.getId() + "-"
+ Long.toHexString(System.currentTimeMillis())
+ "-"
+ Long.toHexString(rnd);
}
The PVS-Studio warning V5307. Potentially predictable seed is used in pseudo-random number generator.
The problem here is that we create a new instance of the Random class each time, causing the generated numbers to be insufficiently random depending on the JDK. To fix this, we could move the creation of the Random instance to the class level and use the same object when needed.
Injection is a vulnerability that arises when untrusted data enters critical points of the application—for example, during SQL query execution. Such data causes the program to behave differently than originally intended. As a consequence, confidential data can be corrupted, disclosed, or program execution can be halted.
Compared to the previous version, this category has dropped from A3 to A5. Injection in the new version of the list still takes place between Cryptographic Failures and Insecure Design (see below). This category likely moved lower due to more extensive testing of applications using various tools. Today, no decent project goes without injection checks.
In our tool, there are quite a few rules that detect defects related to this category. An extensive list of various injections is well-detected by static analysis tools:
For example, let's look at a Cookie injection:
public void ChangeCookie()
{
String cookieValue = Request.Form["userRole"];
Response.Cookies.Add(
new HttpCookie(WebLocalizationConfiguration.CookieName, cookieValue)
{
Expires = Clock.Now.AddYears(2),
Path = Request.ApplicationPath
}
);
....
}
A new object of the HttpCookie class is added to HttpResponse, which is initialized based on data from an external source, Request.Form. Using data without any verification or validation can allow attackers to affect the application behavior.
We could fix the situation using, for example, the following check:
....
String cookieValue = Request.Form["userRole"];
if (!Regex.IsMatch(cookieValue, DataValidationPattern))
return;
....
Insecure Design is a category that covers vulnerabilities that are baked in even before the first line of code is written. It deals with shortcomings in application architecture and design: lack of threat modeling, ignoring secure-by-design principles, weak business logic, or lack of integrity control for critical operations.
Unlike implementation errors, which can be fixed through refactoring or patches, architectural flaws can't be eliminated without redesigning the system. Even perfectly written code based on an initially insecure architecture will remain vulnerable.
The Insecure Design category first appeared in the OWASP Top 10 in 2021 as A04 and moved to position A06 in 2025. This doesn't mean the problem has become less relevant. Rather, other risks have come to the fore—such as those related to supply chains or exception handling.
Although static analysis can't directly see the absence of threat modeling, it can detect symptoms of insecure design.
Suppose a banking application implements a money transfer function as follows:
public void transferMoney(
String fromAccountId,
String toAccountId,
BigDecimal amount
) {
Account from = accountRepository.findById(fromAccountId);
Account to = accountRepository.findById(toAccountId);
from.debit(amount);
to.credit(amount);
}
At first glance, everything seems logical. However, the code lacks necessary checks.
Such an architecture initially lacks protection against fraud, overdraft, or CSRF-like attacks. This is a classic case of Insecure Design.
PVS-Studio can help detect some manifestations of such design problems. But static analysis can't directly indicate that an application is incorrectly designed.
This group of vulnerabilities related to errors in session or user authentication management. Typically, such vulnerabilities can lead to the compromise of passwords, security keys, or session tokens, and also allow an attacker to access someone else's data.
In the previous version of OWASP Top 10, this category was called Identification and Authentication Failures and took the same 7th position. In this version, as you may have noticed, the name has shifted towards authentication, to align more accurately with the list of CWEs for this category.
As an example of a defect from this category, look this abstract case:
public static void main(String[] arg)
{
....
JSch jsch = new JSch();
Session session = jsch.getSession(user, host, 22);
session.setPassword("123fj");
....
}
"Yeah, why not just save the password right in the code?.. Wait, you mean it's easy to extract it from there?.. And why am I suddenly responsible if someone exploits it?.."
To avoid hearing such questions, simply use secure storage where data is kept encrypted. And don't forget about access control for such storage. A regular user shouldn't be able to access it in any way.
public static void main(String[] arg)
{
....
JSch jsch = new JSch();
Session session = jsch.getSession(user, host, 22);
session.setPassword(dataStorage.getPassword);
....
}
PVS-Studio analyzer detects such errors using diagnostic rule V5305: Storing credentials inside source code can lead to security issues, and for C# projects there is a similar rule V5601.
Software or Data Integrity Failures is a group of vulnerabilities that lead to a violation of software integrity. Updates without digital signatures, insecure deserialization, loading dependencies from insecure repositories, etc.
This category, like the previous one, hasn't changed its position in the list but slightly altered its name from Software and Data Integrity Failures for greater clarity.
Let's take insecure deserialization as an example.
Attacks carried out through deserialization of external data can have various goals and implementation options. For instance, an attacker can tamper with data in the deserialized object, which may lead to unauthorized privilege escalation or the assignment of an invalid value to some object field.
The best way to avoid such cases is to prevent users from sending data to the application for serialization. But if it's still necessary, they should use secure libraries for serialization/deserialization and also validate external data.
Let's take the following configuration:
public void notSecure(HttpServletRequest req,
HttpServletResponse res) throws .... {
ServletInputStream servletIS = req.getInputStream();
ObjectInputStream objectIS = new ObjectInputStream(servletIS); // <=
Object object = objectIS.readObject();
}
It's not secure. The code below is much better:
class SecureObjectInputStream extends ObjectInputStream {
public SecureObjectInputStream(InputStream in) throws IOException {
super(in);
}
@Override
protected Class<?> resolveClass(ObjectStreamClass osc) throws .... {
List<String> allowedClasses = new ArrayList<>();
allowedClasses.add(AllowedClass1.class.getName());
allowedClasses.add(AllowedClass2.class.getName());
if (!allowedClasses.contains(osc.getName())) {
throw new InvalidClassException("Unauthorized deserialization",
osc.getName());
}
return super.resolveClass(osc);
}
}
....
public void withCheck(HttpServletRequest req,
HttpServletResponse res) throws .... {
ServletInputStream servletIS = req.getInputStream();
ObjectInputStream objectIS = new SecureObjectInputStream(servletIS);
Object object = objectIS.readObject();
}
In this code version, we created the SecureObjectInputStream class, derived from ObjectInputStream. We overrode the resolveClass method, which enables us to check which type of the object we are about to deserialize. The behavior described in the method prevents us from deserializing an unsafe object.
PVS-Studio analyzer detects such defects using diagnostic rule V5333: Possible insecure deserialization vulnerability. Potentially tainted data is used to create an object during deserialization. For C# projects, there's a similar V5611 diagnostic rule.
This category combines shortcomings in audit, monitoring, and alerting systems that hinder the detection, investigation, and response to security incidents. Unlike the previous OWASP Top 10 (2021), which discussed Security Logging and Monitoring Failures, in 2025 the emphasis shifted from passive event recording to active alerting and response, that's why the name changed to "Alerting."
The problem is not that logs aren't written. Here is the list of issues below.
A static analyzer won't configure logging in a project. Still there are cases where PVS-Studio can help with this category—at least with preserving log integrity.
For example, we need to log user input, but we do it without checking data:
public class InputHelper {
private final Logger logger = LoggerFactory.getLogger(InputHelper.class);
private String username;
public void process(InputStream stream) {
Scanner scanner = new Scanner(stream);
String userInput = scanner.next();
String logMessage = "User '" + userName +
"' entered value: '" + userInput + "'.";
logger.info(logMessage); // <=
}
}
In this case, an attacker can inject random data about events that never occurred and mislead the person analyzing the logs. For example, an attacker might enter this value:
2022/r/nINFO: User 'Admin' logged out.
Then we'll see the following result in logs:
INFO: User 'SomeUser' entered value: '2022'.
INFO: User 'Admin' logged out.
Detecting such situations is possible with rule V5319: Possible log injection. Potentially tainted data is written into logs, or the similar rule for C# projects, V5619.
Last but not least, here is another completely new category in the OWASP Top 10 2025. For the first time in OWASP Top 10 history, attention is given not only to what code does but also to what happens when it breaks. Situations where an application either ignores errors or reacts to them by creating new vulnerabilities are quite common.
Such defects can lead to the following consequences:
Static analysis is quite effective at detecting such problems because many of them manifest as anti-patterns in the code.
Here is a simple case as an example:
....
try {
authService.authenticate(user, password);
} catch (AuthenticationException e) {}
....
It seems like we just didn't handle the Exception, but because of this, a user suddenly might have been authorized by default.
Here is another unwanted scenario where we replace one exception with another:
....
try {
processPayment();
} catch (Exception e) {
throw new RuntimeException("Payment failed");
}
....
In this case, we lose the stack trace of the original exception, hindering our ability to find the source of the problem.
The topic of exceptions is so vast that sometimes mistakes appear to be quite amusing. Look at the fragment from the Apache NiFi project:
public void finishTransferFlowFiles(
final CommunicationsSession commSession
) throws IOException {
if (postResult == null) {
new IllegalStateException(....);
}
....
}
The PVS-Studio warning: V5303 The object was created but it is not being used. The 'throw' keyword could be missing. The 'throw' keyword could be missing.
We handled a specific situation when postResult is null and even created the IllegalStateException we needed. One problem—we forgot to throw it :).
So, the new category is a treasure trove of various interesting issues that a static analyzer, such as PVS-Studio, can help find.
OWASP Top 10:2025 is more than just an updated list of threats. It reflects how the very nature of attacks is changing: from classic injections to supply chain compromise, from code errors to design failures, from silent logs to dangerously ignored failures.
The two new categories—Software Supply Chain Failures (A03) and Mishandling of Exceptional Conditions (A10)—particularly clearly show that security is no longer limited to just clean code. It encompasses the entire software lifecycle: from choosing dependencies and architectural decisions to reacting to exceptions.
Static analysis remains one of the most effective tools for early risk detection. Of course, not all OWASP Top 10 categories can be covered directly by SAST—especially those related to configuration, CI/CD, or human processes. But even where the analyzer doesn't give a direct answer, it helps detect symptoms of poor security practices: lack of validation, weak checks, unprotected entry points.
You can try PVS-Studio static analyzer using a free license, available here. We've looked at various scenarios where the analyzer helps find security defects described in the OWASP Top 10, so why not try checking your project for them? :)
Security is not a feature you can turn on. It's a discipline built into every stage of development. The later we start introducing it, the more expensive this neglecting becomes.
Memento Securitatis.
0