Safeguard
Product•

SAST for Java in Safeguard: Dataflow Traces and CWE Mapping

Safeguard SAST scans Java source with no build step, traces untrusted input from source to sink, and maps each finding to CWE and OWASP.

Safeguard Security Team
39 min read

Safeguard SAST is generally available, and Java is one of its three phase one languages, alongside JavaScript/TypeScript and Python. It reads your Java source without building or running it, follows untrusted input from a source (such as a request parameter) to a dangerous sink (such as a SQL query, a command execution or a file path), and reports each finding with the full dataflow trace, a CWE and OWASP mapping, a severity and the code location. You can run it against a connected repository, from the Safeguard CLI, or on a local runner so your source never leaves your own machines.

This post covers what the engine is documented to do, how to set it up, how to read a Java trace that crosses a controller, a service and a repository class, how to run a pilot, and the limits today.

Key takeaways

  • SAST (GA) is Safeguard's first-party static analysis engine. Java is in the first wave of languages. The docs say detection depth "expands over time", so treat coverage as growing, not complete.
  • Every SAST finding carries a source to sink dataflow trace, a CWE and OWASP mapping, a severity and a code location. The trace is what makes a Java finding reviewable, because the input and the vulnerable call are usually in different files.
  • No build is required. You point the engine at a repository or a source directory. You do not need a working Maven or Gradle build to get results.
  • It runs from the Integrations page (add a Safeguard SAST integration), from the Safeguard CLI (GA), or on the local runner (GA), which uploads results and never your source. On-prem and air-gapped deployment (GA) is available for teams that need it.
  • Findings land in the project's Findings tab, filterable to SAST, and in the Application Security panel. SAST shares a unified findings model with DAST (GA), so a runtime issue can be linked to the source code sink that caused it. AutoTriage (GA) collapses duplicates across engines.
  • Findings can feed policies and gates (GA). For the first few weeks, start with warn rather than block.
  • SAST is not part of the free entry tier, which is SCA only. SCA, reachability analysis and autonomous remediation (all GA; remediation ships switched off) cover the third party libraries on your classpath, which SAST does not.

What SAST for Java actually means

Static application security testing reads code without executing it. The useful kind, and the kind Safeguard documents, is taint tracking: the engine marks data that enters from outside the program as untrusted, follows it as it is assigned, passed, concatenated and returned, and raises a finding when it reaches an operation that would be dangerous with attacker controlled data. The entry point is the source. The dangerous operation is the sink. The path between them is the dataflow trace.

Safeguard's application security testing page gives the examples in plain terms. Sources include a request parameter, a command line argument and a file read. Sinks include a SQL query, a command execution and a file path. The engine follows input "across functions and files", which is the part that matters most for Java.

A typical Java web service is layered. A controller method receives the HTTP request. It hands values to a service class, which applies business rules and calls a repository or data access object, which finally talks to the database. The request parameter and the executeQuery call can be four files and three method calls apart. A scanner that only flags the line containing executeQuery leaves the developer to reconstruct that path by hand, and most will not. A scanner that shows each hop lets the developer see in a minute whether the value was validated, bound or concatenated on the way.

For the theory in more depth, our explainer on what taint analysis is covers sources, sinks, sanitisers and propagation in general terms.

The problem: Java injection flaws are old, well understood and still shipping

Injection in Java is not a new problem, and the fixes are not secret. Yet the same classes of flaw keep appearing in new code, in new services and in code written quickly by people and by assistants. The public data is consistent on this.

What the OWASP Top 10 says

The OWASP Top 10:2025 lists A05:2025 Injection. OWASP notes that injection fell two places, from third to fifth, but that 100% of the applications in its data set were tested for some form of injection, that the category maps to 37 CWEs, and that it had the greatest number of CVEs of any category. OWASP singles out cross-site scripting with more than 30,000 CVEs and SQL injection with more than 14,000. In the 2021 edition the same category was A03:2021 Injection, mapped to 33 CWEs, with 94% of applications tested.

The 2025 list also places A03:2025 Software Supply Chain Failures high in the ranking. For a Java team that is a useful reminder that the two halves of the problem are separate: your own code (what SAST reads) and the libraries you pull from Maven Central (what SCA reads). You need both.

What the CWE Top 25 says

MITRE's 2025 CWE Top 25 Most Dangerous Software Weaknesses puts several classes that matter in Java services near the top:

2025 rankCWEWeaknessWhy it matters in Java
1CWE-79Cross-site scriptingServlets, template engines and JSON endpoints that echo input into HTML
2CWE-89SQL injectionJDBC Statement, string built JPQL or HQL, native queries
6CWE-22Path traversalFile downloads, uploads, report exports built from user supplied names
9CWE-78OS command injectionRuntime.exec and ProcessBuilder wrappers around system tools
10CWE-94Code injectionScripting engines and expression evaluators fed with user input
15CWE-502Deserialization of untrusted dataObjectInputStream, XML and JSON binders that instantiate types
22CWE-918Server-side request forgeryURL fetchers, webhooks, image and document importers

The same list puts CWE-862 Missing Authorization at fourth. That is a weakness static taint tracking is poorly suited to, because the problem is a check that is absent rather than data that flows somewhere it should not. We come back to that in the limits section.

What CISA and the FBI said about SQL injection

On 25 March 2024 CISA and the FBI published a joint Secure by Design Alert, Eliminating SQL Injection Vulnerabilities in Software, prompted by a widely exploited SQL injection flaw in a managed file transfer product. The alert notes that SQL injection has been called an "unforgivable" vulnerability since at least 2007, and that CWE-89 remains prevalent despite effective, well documented mitigations. The message to software makers is that this class should be designed out, not patched one instance at a time.

For a Java team, designing it out means parameterised queries everywhere, and some way to notice when someone writes a string concatenated query anyway. Static analysis in the pull request path is that way.

What NIST SSDF expects

NIST SP 800-218, the Secure Software Development Framework, has two practices that SAST serves directly:

  • PW.7, Review and/or Analyze Human-Readable Code. PW.7.1 asks the organisation to decide whether code review or code analysis should be used, and PW.7.2 asks it to perform the review or analysis against the organisation's secure coding standards and to record and triage every issue found. One of PW.7.2's examples is a static analysis tool whose results a human reviews.
  • PW.8, Test Executable Code. This is the dynamic side. Safeguard's DAST (GA) is the matching engine, and we cover it in a separate post.

RV.1.2 also asks producers to review, analyse or test their software to find previously undetected vulnerabilities, and its first example is configuring the toolchain to run automated code analysis and testing on a regular or continuous basis. NIST maps PW.8.1 and RV.1.2 to section 4(e) of Executive Order 14028. SSDF does not name a tool or require a particular product. It asks you to show that the analysis happens and that the results are triaged.

What the verification standards add

The OWASP Application Security Verification Standard 5.0.0, published on 30 May 2025, lists verifiable requirements for web applications and services. A CWE mapped finding can be traced to the ASVS requirement it breaks, which helps when a customer or auditor asks how you verify a control.

Why Java is hard for static analysis

Four things about Java make static analysis harder, and they are worth knowing before you read your first results.

  • Annotations route requests. In most Java web frameworks there is no explicit call from a main method to your handler. A routing annotation on a method declares it as an entry point, and the parameters bound from the request are the sources. An engine has to understand that a method parameter annotated as a request parameter, header or body is untrusted input.
  • Dependency injection wires the layers at runtime. A controller calls a service through an interface. The implementation is chosen by the container. A static engine has to resolve that call to the right class or it loses the trail.
  • Queries live inside strings. JPQL, HQL and native SQL are often built as strings and passed to an ORM. The ORM call is the sink, and the question is whether user data was concatenated into the string or bound as a parameter.
  • Reflection, serialisation and scripting bend control flow. Code that loads a class by name, deserialises an object graph or evaluates an expression can run paths that do not appear in the call graph.

Safeguard's docs do not publish a list of the Java frameworks or APIs the engine models. That is common for SAST engines, and it is why the pilot plan later in this post starts with a seeded test branch built from your own frameworks rather than from a generic benchmark.

The Java weakness classes to put in your evaluation

The table below lists the classes most Java teams should test, the shape of the flaw, the standard fix, and what Safeguard's docs say about each one. Where the docs do not name a sink class, the honest position is "test it on your code during the pilot", not "covered".

WeaknessTypical Java shapeStandard fixWhat Safeguard's docs say
SQL injection (CWE-89)A request value concatenated into a Statement query or a JPQL stringPreparedStatement with ? placeholders, or named parameters bound through the ORMSQL queries are a documented sink example
OS command injection (CWE-78)A request value passed into Runtime.exec or a shell string in ProcessBuilderA fixed executable, arguments passed as a list, no shell, an allow list for any value that variesCommand execution is a documented sink example
Path traversal (CWE-22)A file name from the request joined to a base directory and openedResolve the canonical path and reject anything outside the base directory, or map IDs to files server sideFile paths are a documented sink example
Deserialization (CWE-502)Request bytes wrapped in ObjectInputStream and passed to readObjectDo not deserialise untrusted data; where unavoidable, harden the stream with an allow list of classesNot named; test it
XML external entities (CWE-611)An uploaded document parsed with a default DocumentBuilderFactoryDisallow DOCTYPE declarations on the parser factoryNot named; test it
Server-side request forgery (CWE-918)A URL from the request opened with URL.openConnection or an HTTP clientAn allow list of destinations, resolved and checked server sideNot named; test it
Cross-site scripting (CWE-79)A request value written to the servlet response or a template without encodingContext aware output encoding, auto-escaping templatesNot named for Java; test it
Code injection (CWE-94)A request value passed to a scripting or expression engineNever evaluate user input; use a fixed expression with bound variablesNot named; test it

The fixes in the third column come from the OWASP Cheat Sheet Series. The SQL Injection Prevention Cheat Sheet puts prepared statements with parameterised queries first. The XML External Entity Prevention Cheat Sheet gives the disallow-doctype-decl feature for Java parsers. The Deserialization Cheat Sheet has a Java section on hardening your own java.io.ObjectInputStream and on not deserialising domain objects from untrusted sources. MITRE's CWE-502 entry includes a short Java example of the readObject pattern.

How Safeguard handles SAST for Java, step by step

Before the steps, here is what Safeguard's application security testing docs say the SAST engine is, in one table.

AspectWhat the docs say
InputsA connected repository or a local directory. No build required.
Languages (phase 1)JavaScript/TypeScript, Python and Java, with more to follow
What you getA finding with the source to sink dataflow trace, a CWE and OWASP mapping, a severity and a code location
Where it runsIn your pipeline, or on a local runner so source never leaves your perimeter
Where results goYour tenant scoped findings view, in the app
MaturityGA. The docs say SAST and DAST "are rolling out" and detection depth expands over time

The docs also note that the platform wiring, the unified findings model and the DAST safety controls are in place today. What grows is detection depth. Keep that sentence in mind when you plan rollout.

Step 1: Confirm SAST is switched on for your tenant

SAST is one of the newer first-party engines, and each of them is independently feature flagged. On the project's Findings tab, the Application Security panel has a SAST sub-tab gated by a server enforced flag named scanner:sast. An administrator toggles it. If your tenant does not show the SAST sub-tab, or the SAST option in the Scanner dropdown has nothing behind it, ask your Safeguard contact to enable SAST for your tenant before you troubleshoot anything else.

Two practical notes. The Portal free entry tier is SCA only, so SAST is outside it. And agree up front with your Safeguard contact which Java repositories you will pilot on and how you will report false positives and misses.

Step 2: Decide where the scan runs

You have three routes, and for Java the choice is usually about where the source is allowed to go.

  • A connected repository. You connect GitHub, GitLab, Bitbucket or another Git compatible host, select the repository, and the scan runs through the platform. This is the least effort for repositories your security policy already allows a cloud service to read.
  • The Safeguard CLI (GA) in your pipeline or on a workstation. The scan runs where the CLI runs.
  • The local runner (GA). The runner is part of the CLI. It polls the platform for jobs assigned to that machine over outbound HTTPS, runs each job locally and, in the docs' words, uploads only the resulting SBOM and findings, "never your source code". The application security testing page names the runner as the way to keep source inside your perimeter for SAST.

If your organisation needs everything on its own infrastructure, Safeguard also offers on-prem and air-gapped deployment (GA). Our post on choosing between a repo connection, the CLI and the local runner walks through the trade-offs in detail.

Step 3: Add the Safeguard SAST integration

From the Integrations page, add an AppSec integration and choose Safeguard SAST. The docs give two options: select a repository, or point at a source directory. You add it the same way you add a repository or a registry. Scans then trigger through the standard flow of integration, then pipeline, then CLI, and results appear in your findings views.

For a Java repository, think about scope before you click save:

  • Single module services. Point at the repository. The engine reads the source tree.
  • Multi-module Maven or Gradle builds. The docs do not say how the engine treats module boundaries. In the pilot, scan the whole repository once, then scan one module on its own, and compare. If traces that cross modules (a controller in web, a repository class in persistence) only appear in the whole repository scan, keep scanning at the root.
  • Generated sources. Code generated from OpenAPI specs, protocol buffers or annotation processors can produce noise or hide the real entry points. Note where generated code lives so you can recognise it in results.
  • Test code. Tests often contain deliberately unsafe fixtures. Decide whether you want them in scope and record the decision.

Step 4: Or run it from the CLI

The application security testing docs show this command for static analysis of a source directory:

# Static analysis of a source directory
safeguard appsec sast --dir ./src --tenant-id <tenant> --product-id <product>

If you do not have the CLI yet, the docs give a one line installer:

# macOS and Linux
curl -fsSL https://cli.safeguard.sh/install | bash

Sign in with the device code flow before you scan. On a build agent with no browser, use the no browser variant and approve the session from another device:

# Opens your browser; approve the session there
safeguard login

# On a machine with no browser: prints a URL and a code to enter elsewhere
safeguard login --no-browser

# Who am I signed in as, and when does the token expire
safeguard whoami

The CLI usage docs say a machine is authorised by signing in once, and that there is no API key environment variable for the CLI. Plan your build agents accordingly.

For a Java project, the ./src in the documented example is usually your module's src folder, which holds main/java and often test/java. Point the flag at the folder that matches the scope you chose in Step 3.

Two things the docs are explicit about. First, safeguard --help and safeguard appsec sast --help list the flags in the build you have, and if a docs page and --help disagree, --help is right. Second, there is no CLI command for listing SAST findings yet. You run the scan from the CLI and read the results in the app.

Step 5: Or keep the source on your machines with the local runner

For a private Java codebase that must not be read by a cloud service, start the runner in headless mode on a machine that has the checkout. These are the commands the runner docs show:

# Start without the terminal UI and serve assistant turns for this workspace
safeguard runner start --workspace ./path/to/repo

# Is it running, and what is it doing
safeguard runner status

# Drain in-flight work and stop
safeguard runner stop

The runner sends a heartbeat, claims jobs assigned to that machine, runs them with its own local scan server and uploads result files. All traffic is outbound HTTPS that the runner opens. Nothing connects to it. On networks that forbid long held connections, the docs say to pass a poll wait of zero so the runner polls at a fixed interval instead.

Step 6: Find the results

Open the project in Safeguard and go to the Findings tab. At the top of the tab is a compact Scanner dropdown. The default, All scanners, shows every finding type together. Choose SAST and the table, the summary stats and the underlying queries narrow to SAST findings only.

Further down the same tab is the Application Security panel, with one sub-tab per first-party engine. The SAST sub-tab is the one gated by the scanner:sast flag. The docs describe findings in the app's AppSec views as carrying the severity, the rule, the CWE, the location and, for SAST, the dataflow trace.

Every finding is scoped to your tenant and organisation end to end.

Step 7: Read a Java dataflow trace

This is where most of the value is, so it is worth slowing down. Here is a small, deliberately vulnerable Java example split across three classes, the way real services are. It is an illustration for this post, not output from Safeguard.

The controller receives the request:

@GetMapping("/orders")
public List<Order> search(@RequestParam String customer) {
    return orderService.findByCustomer(customer);
}

The service passes the value on, with a check that looks protective but is not:

public List<Order> findByCustomer(String customer) {
    if (customer.isBlank()) {
        return List.of();
    }
    return orderRepository.search(customer.trim());
}

The repository builds the query by concatenation:

public List<Order> search(String customer) throws SQLException {
    String sql = "SELECT * FROM orders WHERE customer = '" + customer + "'";
    try (Statement st = connection.createStatement()) {
        return map(st.executeQuery(sql));
    }
}

A source to sink trace for this shape has four or five hops. Read conceptually, it says:

  1. Source. The customer parameter of search in the controller, bound from the HTTP request. Untrusted.
  2. Call. The value is passed as the argument to findByCustomer in the service. Still untrusted.
  3. Propagation. isBlank() is a check that returns early on empty input. It does not change the value or constrain its characters. trim() returns a new string derived from the input. Still untrusted.
  4. Call. The trimmed value is passed to search in the repository.
  5. Propagation. The value is concatenated into the sql string.
  6. Sink. sql is passed to executeQuery on a Statement. This is the SQL query sink. The weakness is CWE-89, under OWASP A05:2025 Injection.

Safeguard's docs describe the trace as "the ordered hops from source to sink", so a developer can see why the code was flagged and not just where. With the trace in hand, triage becomes four questions:

  • Is the source really attacker controlled? A request parameter is. A value read from your own configuration file usually is not. A value from another internal service sits in between and depends on your trust model.
  • Does any hop actually constrain the value? A length check, a blank check or a trim() does not. A strict allow list or a conversion to a numeric type does. Look at every hop, not just the last one.
  • Is the sink really dangerous with this value? A PreparedStatement with a bound parameter is not a SQL injection sink for that parameter. A Statement with a concatenated string is.
  • Is the code reachable in production? Dead code, feature flagged paths and test utilities change the priority, though they rarely make a true finding false.

Step 8: Triage across engines

SAST findings sit in the same unified findings model as the other engines. The model shares correlation keys across engine types. When a DAST (GA) scan of a verified target confirms a runtime issue, and that issue maps to the SAST source code sink that produced it, the docs say the two are linked and prioritised together, so a confirmed, reachable, exploitable finding rises to the top.

For a Java service with a staging environment, that is the strongest signal you can get. DAST in Safeguard only runs active checks against targets you have verified you own, inside a scope you define. See what Safeguard DAST discovers and what it will not test for how that works.

If you run more than one engine over the same code, AutoTriage (GA) deduplicates across scanners, collapsing the same underlying issue into one correlated finding and reporting how much volume it removed. It is admin toggled through a feature flag.

The docs do not yet describe SAST specific suppression syntax or in-code annotations for marking a finding as accepted. Record triage decisions in the way your process already requires, and ask your Safeguard contact how decisions are kept across rescans.

Step 9: Fix the code

SAST tells you where the flaw is. The fix is still yours to write, and for most Java findings the fix is short. Here is the repository method from Step 7, corrected with a bound parameter:

public List<Order> search(String customer) throws SQLException {
    String sql = "SELECT * FROM orders WHERE customer = ?";
    try (PreparedStatement ps = connection.prepareStatement(sql)) {
        ps.setString(1, customer);
        return map(ps.executeQuery());
    }
}

The same principle applies through an ORM. Use a named parameter in the JPQL string and bind the value, instead of concatenating it. For the other common classes:

  • Command execution. Call a fixed executable with arguments as a list, never through a shell, and check any variable argument against an allow list.
  • Path traversal. Build the path, resolve its canonical form, and reject it unless it starts with the canonical base directory. Better still, take an identifier from the user and look the file up server side.
  • Deserialization. Do not deserialise untrusted bytes into Java objects. Where you cannot avoid it, the OWASP Deserialization Cheat Sheet describes hardening ObjectInputStream so only an allow list of classes can be created.
  • XML parsing. Set the disallow-doctype-decl feature on the parser factory, as the OWASP XXE Prevention Cheat Sheet shows for Java.

After the fix, rescan. A finding that clears after the fix is the cheapest confirmation you will get that the engine and your change agree.

Step 10: Gate merges, carefully

Safeguard's policies, gates and guardrails (GA) enforce rules at several points, including CI, where the guardrails docs describe platform policy gates on the scans of connected repositories. Effects are BLOCK, WARN or AUTO_FIX, and every enforcement decision produces an audit record. The application security testing docs point to guardrails as the way to use findings as policy gates in CI/CD.

For a new engine in your pipeline, start with WARN on new SAST findings of high and critical severity, measure the false positive rate on your Java code for a few weeks, and move to BLOCK only for the classes where the engine has proved accurate on your code, typically SQL injection and command injection. The docs do not yet publish the policy fields for SAST findings, so confirm the exact condition you can write with your Safeguard contact. Our post on turning plain English security policies into release gates covers the policy model itself.

Step 11: Keep it running

Safeguard's continuous scanning docs describe connected source repositories being rescanned on every push, with a scheduled pull to catch missed webhooks. That page is about the platform's asset inventory in general. Confirm with Safeguard whether SAST scans run on those same triggers for your tenant or only when the integration or the CLI runs them, and set your pipeline up to match.

Ask about findings from Claude or ChatGPT

If your team triages in an AI assistant, add Safeguard from the Claude or ChatGPT connector directory rather than wiring up a custom server. The connector signs in with your Safeguard account, applies your tenant scope and reads projects, vulnerabilities and findings. Which tools it exposes depends on your plan and your administrator's settings. Our post on Safeguard in your IDE and in the Claude and ChatGPT connector directories shows the install route.

SAST, SCA and DAST on one Java service

A Java service has three different kinds of exposure, and each engine sees one of them. The table below is a decision aid for the questions teams actually ask.

Question about your Java serviceEngine that answers itLabel
Does any library on our classpath have a known CVE, including transitive dependencies?SCA, deep dependency scanningGA
Is the vulnerable method in that library actually called by our code?Reachability analysisGA
Can we get a tested pull request that moves the library to a fixed version?Autonomous remediation (Griffin fix pull requests), ships switched off with every category on ManualGA
Did one of our developers concatenate a request value into a query?SASTGA
Does an upload handler pass a user supplied name into a file path?SASTGA
Is the deployed staging app missing security headers, or does it respond to an injection probe?DAST, verified targets onlyGA
Is the SAST finding we are looking at actually exploitable at runtime?SAST and DAST linked through correlation keysGA
Should this release be blocked?Policies and gates over the findingsGA

Two Java specific overlaps are worth calling out. A deserialization flaw often needs both halves: your code calls readObject on untrusted input (a SAST question), and a library on the classpath provides the classes an attacker chains together (an SCA question). And a SQL injection finding in a repository class is still your bug even if the ORM you use has no CVE at all. Neither engine replaces the other. For the broader comparison, see SCA vs SAST vs DAST: which do you actually need first, and for how reachability ranks dependency findings, prioritise vulnerabilities with reachability, EPSS and KEV in Safeguard.

A 30 day SAST pilot for a Java estate

Measure any new scanner before you trust it. Here is a pilot plan that fits most Java teams and produces a clear go or no go decision.

Week 1: choose repositories and seed a test branch. Pick two or three repositories that represent your estate. A good mix is one web service on your main framework, one batch or messaging service, and one shared library. In a private branch that will never ship, add a small number of known bad patterns from the weakness table, written in your own house style and routed through your own layers: a concatenated query in a repository class, a ProcessBuilder built from a request value, a file download built from a request parameter, an ObjectInputStream over a request body. Add a matching set of safe variants (a bound parameter, an allow listed command) so you can measure false positives as well as misses.

Week 2: scan and score. Run SAST on the main branch and the seeded branch. For each seeded case, record whether a finding appeared, whether the CWE was right, and whether the trace showed every hop. For the main branch, sample up to 50 findings and have a developer who knows the code mark each one as true, false or unclear.

Week 3: correlate and fix. If you have a staging environment, verify it as a DAST target and scan it, then look for linked SAST and DAST findings. Fix the true positives on the main branch and rescan to confirm they clear.

Week 4: decide. Set warn or block per weakness class based on what you measured.

Pilot measureHow to record itWhat good looks like
Seeded cases detectedFound or missed, per CWEThe classes you plan to gate on are found consistently
Safe variants flaggedCount of findings on the safe versionsClose to zero for the classes you plan to block
Trace completenessDid the trace show every hop from controller to sinkDevelopers can triage without opening five files
CWE accuracyDid the mapping match the weaknessCorrect for each seeded class
Main branch precisionTrue, false, unclear in your sample of 50Enough true findings that developers keep reading them
Time to triageMinutes per finding, medianFalls as the team learns the trace format

Send the misses and the false positives to Safeguard. That is how the engine improves.

What Safeguard does not do

Honest limits, as of this post:

  1. Detection depth is still growing. The docs say SAST and DAST are rolling out and that detection depth expands over time. Safeguard does not publish a list of Java rules, modelled frameworks or sink APIs. Do not tell an auditor or a customer that a specific Java weakness class is covered until you have seen it detected on your own code.
  2. Java is the only JVM language listed for SAST. Phase one is JavaScript/TypeScript, Python and Java. Kotlin, Scala and Groovy are not listed for SAST today. They are covered for dependency analysis by SCA, which is a different question.
  3. It does not list findings from the CLI yet. The docs are explicit that there is no CLI command for listing SAST findings. Results are read in the app.
  4. It does not find what is missing. Taint tracking finds data that flows somewhere dangerous. It is weak at finding checks that are absent, such as a missing authorisation check on an admin endpoint (CWE-862, fourth in the 2025 CWE Top 25) or a business rule that can be skipped. Code review and threat modelling still own those.
  5. It does not prove exploitability. A static finding says a path exists in the code. Whether it is exploitable in production depends on configuration, deployment and runtime controls. DAST against a verified target, and your own testing, answer that.
  6. It does not scan compiled artifacts. SAST reads source code from a repository or directory. A JAR, WAR or container image you received without source is an SCA and SBOM question, not a SAST one.
  7. It does not write your code fix for you. The offering files describe Griffin fix pull requests for dependency upgrades and other remediation categories, and remediation ships switched off. They do not describe automatic code fixes for Java SAST findings, so plan to fix these by hand.
  8. It is not in the free tier. The Portal free entry tier is SCA only.

Common mistakes

Blocking merges on day one. A new scanner on a large Java codebase will produce some findings your developers disagree with. If the first experience is a blocked merge on a false positive, the team will stop reading the results. Warn first, measure, then block selected classes.

Pointing SAST at build output. SAST reads source. Pointing it at a target or build folder full of class files, or at a JAR, gives you nothing useful. Point it at the source tree and use SCA for binaries.

Trusting the last hop. A reviewer sees a validation call in the service class and closes the finding. Read the whole trace. A blank check, a length check or a trim() does not make a value safe for a SQL string. Only binding, strict allow lists or type conversion do.

Escaping instead of binding. Hand written escaping of quotes in a SQL string is fragile and database specific. The OWASP SQL Injection Prevention Cheat Sheet lists prepared statements first for a reason. Fix by binding.

Reading silence as safety. A Java repository with no SAST findings may be clean, or the engine may not yet model the framework it uses. Your seeded test branch is how you tell the difference.

Ignoring test and generated code decisions. Test fixtures and generated clients can fill the findings view with noise, or the team can exclude them and miss a real bug in a generated controller. Decide scope on purpose and write it down.

Running SAST without SCA. A Java service also ships every library on its classpath. A team that rolls out SAST while known exploited CVEs sit unfixed in its dependency tree has its priorities the wrong way round. Run SCA (GA) first or alongside.

FAQ

Does Safeguard SAST support Java?

Yes. Java is one of the three phase one languages listed in Safeguard's application security testing docs, with JavaScript/TypeScript and Python. More languages are planned.

Do I need to build my Maven or Gradle project before scanning?

No. The docs say SAST takes a connected repository or a local directory and that no build is required.

What does a Safeguard SAST finding contain?

A source to sink dataflow trace, a CWE and OWASP mapping, a severity, the rule and a code location. The trace lists the ordered hops from where untrusted input enters to the dangerous operation it reaches.

Which Java vulnerabilities does Safeguard SAST find?

The docs give SQL queries, command execution and file paths as examples of sinks, and request parameters, command line arguments and file reads as examples of sources. They do not publish a full Java rule list. Test the classes you care about, such as deserialization, XXE and SSRF, on your own code during a pilot.

Does my Java source code leave my network?

Not if you use the local runner. It runs the scan on your machine, opens only outbound HTTPS connections and uploads result files, never source. On-prem and air-gapped deployment (GA) is also available. With a connected repository, the platform reads the repository you authorise.

Does Safeguard SAST support Kotlin or Scala?

They are not listed for SAST today. Phase one covers Java, JavaScript/TypeScript and Python. Kotlin and Scala dependencies are covered by SCA (GA).

How is SAST different from SCA for a Java application?

SAST reads the code your team wrote and looks for unsafe data flows. SCA reads your dependency tree, including transitive libraries from Maven Central, and matches components against known vulnerabilities. A Java service needs both.

Can SAST findings block a pull request?

Findings can feed Safeguard's policies and gates (GA), and the docs point to guardrails for using findings as CI gates. Start with warn, measure accuracy on your code, and confirm the exact policy condition with Safeguard before you block.

How does Safeguard link SAST and DAST findings?

Both engines report into one unified findings model with shared correlation keys. When a DAST scan of a verified target confirms a runtime issue that maps to the SAST sink that caused it, the two findings are linked and prioritised together.

The fixes for most Java SAST findings are code changes your team writes. But a Java service's other large exposure, the libraries on its classpath, can often be closed without waiting on anyone, and the section below is the standard way Safeguard does that.

Fix it without waiting for upstream

Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.

1. Autonomous remediation: pull requests, not tickets

When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation categories include dependencies, licenses, security posture and findings, and each one is set separately. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.

2. Gold Open Source: the safe version in seconds, free

gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds.

3. Gold Registry: start from components that are already fixed

Gold Registry provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs and a completed license check, with signed build provenance (signed with Safeguard's key and verifiable with cosign, SLSA Build Level 3 as the design target) and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.

When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. Ask which packages it covers before you plan around it.

Your situationWhat to use
A fixed version existsAutonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version
You need to know today whether you are affectedGold Open Source lookup and "Am I affected?" checker, free and without login
You want vulnerable versions to stop shippingSafeguard policies and gates in CI
Upstream has no fix yet, or will not ship oneA custom Gold request; fork and patch
You want to start clean on new projectsPull hardened components and images from Gold Registry

One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.

See how Safeguard would help

The first demo is a real Safeguard CLI session. It installs the CLI, opens the interactive terminal with safeguard, and asks it to fix a lodash CVE in a repository. The CLI reads the finding and the advisory, asks before any command that changes something, edits package.json to lodash 4.17.21, runs npm install and npm test with approval, prints a summary and pushes nothing, then shows /permissions, /model and /scan .. The repository is JavaScript, but the terminal, sign in and scan flow are the same CLI you would use to run safeguard appsec sast over a Java source tree.

Click through it at your own pace, or open it full screen.

The second demo sets up the local runner on your own machine, approves it with its device code, and scans from the terminal. This is the route to choose when your Java source must stay on your own hardware: the runner does the work locally and uploads results, not code.

Click through it at your own pace, or open it full screen.

To try it on your own code, install the desktop app or CLI from https://safeguard.sh/download and read the setup guides at https://docs.safeguard.sh/docs . If you want to pilot SAST on Java, ask for it to be enabled when you sign up. For more background on the language itself, see Java security explained, and for how false positives are reduced in static analysis generally, static analysis false positive reduction. For every capability and its label in one place, see what Safeguard does today.

Quick check

A SAST trace shows a request parameter passing through a service method that calls trim() and isBlank() before it reaches Statement.executeQuery in a repository class. Is the finding a false positive?

No. Neither call constrains the characters in the value, so it is still untrusted when it is concatenated into the SQL string. The fix is to use a PreparedStatement and bind the value as a parameter.

Your team wants to scan a vendor's Java JAR file with Safeguard SAST. Is that the right engine?

No. Safeguard SAST reads source code from a repository or directory. For a compiled artifact without source, use SCA and SBOM analysis (GA) to identify its components and their known vulnerabilities.

Sources

Last reviewed: 24 October 2026. Product capabilities change; check docs.safeguard.sh for the current state.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.