Fortify SCA for Java: Complete Security Scanning Guide


Article

Fortify Static Code Analyzer (SCA) is a powerful static application security testing (SAST) tool that identifies security vulnerabilities in source code. For Java applications, it provides deep security analysis across the entire codebase, identifying issues like injection flaws, cryptographic weaknesses, and access control problems.

This guide covers everything from basic setup to advanced integration and remediation strategies.

Fortify SCA Architecture Overview

  • Source Analyzer: Parses code and builds intermediate representation
  • Scan Engine: Applies security rules to identify vulnerabilities
  • Audit Workbench: GUI for reviewing and prioritizing issues
  • Software Security Center: Centralized management and reporting
  • Custom Rules: Extend analysis with organization-specific rules

1. Installation and Setup

System Requirements:

  • Windows/Linux/macOS
  • Java 8 or higher
  • 4GB+ RAM (8GB+ recommended)
  • 10GB+ disk space

Installation Steps:

  1. Download Fortify SCA from Micro Focus website
  2. Run installer with administrative privileges
  3. Configure license server connection
  4. Set up environment variables

Environment Configuration:

# Add to .bashrc or equivalent
export FORTIFY_HOME=/opt/Fortify/Fortify_SCA_and_Apps_23.1.0
export PATH=$FORTIFY_HOME/bin:$PATH
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk
export PATH=$JAVA_HOME/bin:$PATH
# Fortify specific variables
export FORTIFY_SCA_HOME=$FORTIFY_HOME
export SCA_VM_OPTS="-Xmx4G -Xms2G"

2. Basic Scanning Workflow

Command Line Interface Basics:

# Basic scan
sourceanalyzer -b myapp build.sh
sourceanalyzer -b myapp -scan -f myapp.fpr
# Maven project scan
sourceanalyzer -b myapp mvn compile
sourceanalyzer -b myapp -scan -f myapp.fpr
# Gradle project scan
sourceanalyzer -b myapp gradle build
sourceanalyzer -b myapp -scan -f myapp.fpr

Maven Integration:

<!-- Fortify Maven Plugin -->
<plugin>
<groupId>com.fortify</groupId>
<artifactId>fortify-maven-plugin</artifactId>
<version>1.0</version>
<configuration>
<scaHome>/opt/Fortify/Fortify_SCA_and_Apps_23.1.0</scaHome>
<buildId>${project.artifactId}</buildId>
<maxHeap>4G</maxHeap>
</configuration>
</plugin>
# Maven commands
mvn fortify:clean
mvn fortify:translate
mvn fortify:scan

3. Comprehensive Build Integration

Maven Full Configuration:

<profile>
<id>fortify-scan</id>
<build>
<plugins>
<plugin>
<groupId>com.fortify</groupId>
<artifactId>fortify-maven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<id>fortify-scan</id>
<phase>verify</phase>
<goals>
<goal>clean</goal>
<goal>translate</goal>
<goal>scan</goal>
</goals>
</execution>
</executions>
<configuration>
<scaHome>${env.FORTIFY_HOME}</scaHome>
<buildId>${project.artifactId}-${project.version}</buildId>
<maxHeap>8G</maxHeap>
<jvmOptions>
<jvmOption>-Xmx8G</jvmOption>
<jvmOption>-Xms2G</jvmOption>
</jvmOptions>
<source>1.8</source>
<failOnError>false</failOnError>
<verbose>true</verbose>
<debug>false</debug>
<excludeList>fortify-excludes.txt</excludeList>
<filterFile>fortify-filters.fdl</filterFile>
<rules>custom-rules.xml</rules>
</configuration>
</plugin>
</plugins>
</build>
</profile>

Gradle Integration:

// build.gradle
plugins {
id 'com.fortify.fortify-gradle-plugin' version '1.0'
}
fortify {
scaHome = System.getenv('FORTIFY_HOME')
buildId = "${project.name}-${project.version}"
maxHeap = '8G'
source = '1.8'
exclude = file('fortify-excludes.txt')
filterFile = file('fortify-filters.fdl')
}
task fortifyScan(dependsOn: 'fortifyScan') {
doLast {
println "Fortify scan completed for ${project.name}"
}
}

4. Advanced Scanning Configuration

Custom Build Configuration:

#!/bin/bash
# fortify-scan.sh
PROJECT_NAME="my-application"
SCAN_DIR="./src"
OUTPUT_FILE="${PROJECT_NAME}.fpr"
EXCLUDE_PATTERNS="**/test/**,**/generated/**,**/target/**"
MAX_HEAP="8G"
echo "Starting Fortify scan for ${PROJECT_NAME}"
# Clean previous scan
sourceanalyzer -b ${PROJECT_NAME} -clean
# Translate source code
sourceanalyzer -b ${PROJECT_NAME} \
-source 1.8 \
-cp "./target/classes:./target/dependency/*" \
-exclude "${EXCLUDE_PATTERNS}" \
${SCAN_DIR}
# Perform scan
sourceanalyzer -b ${PROJECT_NAME} \
-scan \
-f ${OUTPUT_FILE} \
-maxheap ${MAX_HEAP} \
-rules custom-rules.xml \
-filter fortify-filters.fdl \
-format fpr
echo "Scan completed: ${OUTPUT_FILE}"

Exclusion Configuration (fortify-excludes.txt):

# Exclude test directories
**/test/**
**/test/**/*
**/test*/**
**/*Test.java
**/*Tests.java
# Exclude generated code
**/generated/**
**/target/generated-sources/**
# Exclude third-party libraries
**/lib/**
**/dependency/**
# Exclude specific packages
**/com/example/legacy/**
**/com/example/experimental/**
# File patterns to exclude
*.min.js
*.css
*.properties
*.xml

5. Custom Rule Development

Custom Rule Template:

<!-- custom-rules.xml -->
<RulePack xmlns="xmlns://www.fortify.com/schema/rules">
<RulePackID>B5C0D3F0-9E2A-4D8C-9C7A-3F6E8B2A1C7D</RulePackID>
<Name>MyCompany Custom Rules</Name>
<Version>1.0</Version>
<Description>Custom security rules for MyCompany applications</Description>
<Rules>
<Rule Definition="D8D7916A-4B6C-4A7D-9C3A-2E8F4B1A6D9C" Language="Java">
<Group>Custom Security</Group>
<Category>Custom Vulnerability</Category>
<Folder>MyCompany Rules</Folder>
<Kingdom>Code Quality</Kingdom>
<DefaultSeverity>3.0</DefaultSeverity>
<Abstract>Custom sensitive data exposure</Abstract>
<Description>
Detects exposure of sensitive company-specific data patterns.
</Description>
<RuleBody>
<Unified>
<Context>method</Context>
<Condition>
<Or>
<And>
<Match>method-name:.*[Ss]ensitive.*</Match>
<Match>code-pattern:.*[Pp]assword.*|[Ss]ecret.*|[Kk]ey.*</Match>
</And>
<Match>code-pattern:log\.(debug|info|warn|error)\(.*[Pp]assword.*\)</Match>
</Or>
</Condition>
<Vulnerability>Custom.Sensitive.Data.Exposure</Vulnerability>
<Recommendation>
Avoid logging sensitive information. Use masking or avoid exposure.
</Recommendation>
<Tips>
<Tip>Review logging statements for sensitive data exposure</Tip>
</Tips>
</Unified>
</RuleBody>
</Rule>
<Rule Definition="A1B2C3D4-E5F6-4A7B-8C9D-0E1F2A3B4C5D" Language="Java">
<Group>API Security</Group>
<Category>Insecure Deserialization</Category>
<Folder>MyCompany Rules</Folder>
<Kingdom>Input Validation and Representation</Kingdom>
<DefaultSeverity>4.0</DefaultSeverity>
<Abstract>Insecure Jackson deserialization</Abstract>
<Description>
Detects unsafe Jackson deserialization configurations.
</Description>
<RuleBody>
<Unified>
<Context>class</Context>
<Condition>
<And>
<Match>type-name:.*[Dd]eserializer</Match>
<Match>code-pattern:enableDefaultTyping|@JsonTypeInfo.*USE_CLASS</Match>
</And>
</Condition>
<Vulnerability>API.Insecure.Deserialization</Vulnerability>
<Recommendation>
Avoid using enableDefaultTyping() in Jackson ObjectMapper.
Use @JsonTypeInfo with JsonTypeInfo.Id.NAME instead.
</Recommendation>
<References>
<Reference>https://cowtowncoder.medium.com/on-jackson-cves-dont-panic-here-is-what-you-need-to-know-54cd0d6e8062</Reference>
</References>
</Unified>
</RuleBody>
</Rule>
</Rules>
</RulePack>

Java-Specific Custom Rules:

<!-- java-custom-rules.xml -->
<Rule Definition="CUSTOM_SQL_INJECTION" Language="Java">
<Group>Data Validation</Group>
<Category>SQL Injection</Category>
<Folder>Custom SQL Rules</Folder>
<Kingdom>Input Validation and Representation</Kingdom>
<DefaultSeverity>4.5</DefaultSeverity>
<Abstract>Custom SQL injection detection</Abstract>
<Description>
Detects potential SQL injection with custom query builders.
</Description>
<RuleBody>
<Unified>
<Context>method-call</Context>
<Condition>
<And>
<Match>function:java.sql.Statement.executeQuery</Match>
<Or>
<Match>code-pattern:".*" \+.*[Uu]ser.*[Ii]nput.*</Match>
<Match>code-pattern:String\.format\(.*[Ss]elect.*,%s\)</Match>
</Or>
</And>
</Condition>
<Vulnerability>Custom.SQL.Injection</Vulnerability>
<Recommendation>
Use PreparedStatement with parameterized queries instead of string concatenation.
</Recommendation>
</Unified>
</RuleBody>
</Rule>

6. Filter Configuration

Filter File (fortify-filters.fdl):

# Filter out low priority issues
Filter "MyCompany Security Filter"
Issue_Type "*"
Kingdom "Environment"
Folder "Best Practice"
Confidence "Low"
Analysis "Not an Issue"
EndFilter
# Filter specific false positives
Filter "Spring Framework False Positives"
Issue_Type "Cross-Site Scripting: Persistent"
Folder "Spring Framework"
File_Path "*SpringFramework*"
Analysis "Not an Issue"
EndFilter
# Filter test code issues
Filter "Test Code Filter"
Issue_Type "*"
File_Path "*/test/*"
Analysis "Not an Issue"
EndFilter
# Filter third-party library issues
Filter "Third-Party Libraries"
Issue_Type "*"
File_Path "*/lib/*"
Analysis "Not an Issue"
EndFilter
# Custom filter for business logic false positives
Filter "Business Logic Exceptions"
Issue_Type "Privacy Violation"
File_Path "*UserService*"
Analysis "Not an Issue"
Comment "Business requirement to process user data"
EndFilter

7. CI/CD Integration

Jenkins Pipeline:

// Jenkinsfile
pipeline {
agent any
environment {
FORTIFY_HOME = '/opt/Fortify/Fortify_SCA_and_Apps_23.1.0'
SSC_URL = 'https://ssc.company.com'
SSC_APP_ID = '12345'
SSC_APP_VERSION_ID = '67890'
}
stages {
stage('Build') {
steps {
sh 'mvn clean compile -DskipTests'
}
}
stage('Fortify Scan') {
steps {
script {
// Translate source code
sh """
${FORTIFY_HOME}/bin/sourceanalyzer \
-b ${env.JOB_BASE_NAME} \
-clean
${FORTIFY_HOME}/bin/sourceanalyzer \
-b ${env.JOB_BASE_NAME} \
-source 1.8 \
-cp "target/classes:target/dependency/*" \
-exclude "**/test/**,**/target/generated-sources/**" \
"src/main/java"
"""
// Perform scan
sh """
${FORTIFY_HOME}/bin/sourceanalyzer \
-b ${env.JOB_BASE_NAME} \
-scan \
-f ${env.JOB_BASE_NAME}.fpr \
-maxheap 8G \
-rules custom-rules.xml
"""
// Upload to Software Security Center
sh """
${FORTIFY_HOME}/bin/fortifyclient \
uploadFPR \
-file ${env.JOB_BASE_NAME}.fpr \
-url ${SSC_URL} \
-applicationId ${SSC_APP_ID} \
-applicationVersionId ${SSC_APP_VERSION_ID} \
-authtoken ${SSC_AUTH_TOKEN}
"""
}
}
}
stage('Security Gate') {
steps {
script {
// Check if scan passed security gates
sh """
${FORTIFY_HOME}/bin/fortifyclient \
getIssueCounts \
-url ${SSC_URL} \
-applicationVersionId ${SSC_APP_VERSION_ID} \
-authtoken ${SSC_AUTH_TOKEN} \
-output issue-counts.json
"""
// Parse results and fail if critical issues found
def issueCounts = readJSON file: 'issue-counts.json'
if (issueCounts.Critical > 0) {
error "Build failed: ${issueCounts.Critical} critical security issues found"
}
}
}
}
}
post {
always {
// Archive FPR file
archiveArtifacts artifacts: '*.fpr', fingerprint: true
// Publish Fortify results
fortifyPublisher failBuildOnSeverity: 'Critical'
}
}
}

GitHub Actions Workflow:

# .github/workflows/fortify-scan.yml
name: Fortify Security Scan
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
fortify-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Java
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
- name: Cache Fortify dependencies
uses: actions/cache@v3
with:
path: ~/.fortify
key: ${{ runner.os }}-fortify-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-fortify-
- name: Download Fortify SCA
run: |
# Download Fortify SCA (requires enterprise setup)
curl -L -o fortify-scanner.zip "${{ secrets.FORTIFY_DOWNLOAD_URL }}"
unzip fortify-scanner.zip -d /opt/fortify
- name: Build project
run: mvn -B compile -DskipTests
- name: Run Fortify Scan
env:
FORTIFY_HOME: /opt/fortify
SSC_TOKEN: ${{ secrets.SSC_TOKEN }}
run: |
$FORTIFY_HOME/bin/sourceanalyzer -b ${{ github.event.repository.name }} -clean
$FORTIFY_HOME/bin/sourceanalyzer -b ${{ github.event.repository.name }} \
mvn compile -DskipTests
$FORTIFY_HOME/bin/sourceanalyzer -b ${{ github.event.repository.name }} \
-scan -f scan-results.fpr
# Upload to Software Security Center
$FORTIFY_HOME/bin/fortifyclient uploadFPR \
-file scan-results.fpr \
-url https://ssc.company.com \
-authtoken $SSC_TOKEN \
-applicationVersionId ${{ secrets.SSC_APP_VERSION_ID }}
- name: Check Security Gate
run: |
# Custom script to check security compliance
python scripts/check-security-gate.py scan-results.fpr

8. Remediation Guidance

Common Java Vulnerabilities and Fixes:

SQL Injection:

// VULNERABLE
@GetMapping("/users")
public List<User> getUsers(@RequestParam String name) {
String sql = "SELECT * FROM users WHERE name = '" + name + "'";
return jdbcTemplate.query(sql, userMapper);
}
// SECURE - Use PreparedStatement
@GetMapping("/users")
public List<User> getUsers(@RequestParam String name) {
String sql = "SELECT * FROM users WHERE name = ?";
return jdbcTemplate.query(sql, new Object[]{name}, userMapper);
}
// SECURE - Use JPA
@GetMapping("/users")
public List<User> getUsers(@RequestParam String name) {
return userRepository.findByName(name);
}

Cross-Site Scripting (XSS):

// VULNERABLE
@Controller
public class WelcomeController {
@GetMapping("/welcome")
public String welcome(@RequestParam String name, Model model) {
model.addAttribute("name", name); // Unsafe!
return "welcome";
}
}
// SECURE - Use HTML escaping
@Controller
public class WelcomeController {
@GetMapping("/welcome")
public String welcome(@RequestParam String name, Model model) {
model.addAttribute("name", StringEscapeUtils.escapeHtml4(name));
return "welcome";
}
}
// SECURE - Use Thymeleaf (auto-escaped)
@Controller
public class WelcomeController {
@GetMapping("/welcome")
public String welcome(@RequestParam String name, Model model) {
model.addAttribute("name", name); // Thymeleaf auto-escapes
return "welcome";
}
}

Insecure Deserialization:

// VULNERABLE
public Object deserialize(byte[] data) throws Exception {
ByteArrayInputStream bis = new ByteArrayInputStream(data);
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject(); // Dangerous!
}
// SECURE - Use safe deserialization
public Object deserializeSafe(byte[] data) throws Exception {
// Use validation and filtering
if (!isSafeToDeserialize(data)) {
throw new SecurityException("Untrusted data");
}
// Use look-ahead deserialization
try (ObjectInputStream ois = new SafeObjectInputStream(
new ByteArrayInputStream(data))) {
return ois.readObject();
}
}
// Jackson safe configuration
@Bean
public ObjectMapper objectMapper() {
return JsonMapper.builder()
.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE)
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
.build();
}

9. Reporting and Metrics

Custom Report Generation:

#!/bin/bash
# generate-reports.sh
FPR_FILE=$1
REPORT_DIR="./reports"
# Generate HTML report
ReportGenerator -format HTML -f "$REPORT_DIR/vulnerabilities.html" -source "$FPR_FILE"
# Generate PDF report
ReportGenerator -format PDF -f "$REPORT_DIR/vulnerabilities.pdf" -source "$FPR_FILE"
# Generate CSV for further analysis
ReportGenerator -format CSV -f "$REPORT_DIR/vulnerabilities.csv" -source "$FPR_FILE"
# Generate BIRT report
ReportGenerator -format BIRT -f "$REPORT_DIR/vulnerabilities.rptdesign" -source "$FPR_FILE"
echo "Reports generated in $REPORT_DIR"

Metrics Collection Script:

#!/usr/bin/env python3
# fortify-metrics.py
import xml.etree.ElementTree as ET
import json
import csv
from datetime import datetime
def parse_fpr_metrics(fpr_file):
"""Extract metrics from FPR file"""
# Extract audit.xml from FPR (FPR is a zip file)
import zipfile
with zipfile.ZipFile(fpr_file, 'r') as zip_ref:
audit_data = zip_ref.read('audit.xml')
root = ET.fromstring(audit_data)
metrics = {
'timestamp': datetime.now().isoformat(),
'total_issues': 0,
'by_severity': {},
'by_category': {},
'by_kingdom': {},
'critical_issues': 0,
'high_issues': 0,
'medium_issues': 0,
'low_issues': 0
}
# Parse issues
for issue in root.findall('.//Issue'):
severity = issue.get('severity')
category = issue.get('category')
kingdom = issue.get('kingdom')
# Update counts
metrics['total_issues'] += 1
metrics['by_severity'][severity] = metrics['by_severity'].get(severity, 0) + 1
metrics['by_category'][category] = metrics['by_category'].get(category, 0) + 1
metrics['by_kingdom'][kingdom] = metrics['by_kingdom'].get(kingdom, 0) + 1
# Severity counts
if severity == '5.0' or severity == 'Critical':
metrics['critical_issues'] += 1
elif severity == '4.0' or severity == 'High':
metrics['high_issues'] += 1
elif severity == '3.0' or severity == 'Medium':
metrics['medium_issues'] += 1
else:
metrics['low_issues'] += 1
return metrics
def generate_metrics_report(metrics, output_file):
"""Generate metrics report in JSON format"""
with open(output_file, 'w') as f:
json.dump(metrics, f, indent=2)
if __name__ == "__main__":
fpr_file = "scan-results.fpr"
metrics = parse_fpr_metrics(fpr_file)
generate_metrics_report(metrics, "fortify-metrics.json")

10. Best Practices and Optimization

Performance Optimization:

# Optimized scan command
sourceanalyzer -b myapp \
-Xmx8G \
-Xms4G \
-XX:+UseG1GC \
-Dcom.fortify.sca.Phase0HigherOrderFuncs=true \
-Dcom.fortify.sca.EnableParallelAnalysis=true \
-Dcom.fortify.sca.ThreadCount=4 \
-scan -f optimized-scan.fpr

Incremental Scanning:

#!/bin/bash
# incremental-scan.sh
PROJECT="myapp"
LAST_SCAN="last_scan.timestamp"
# Check if we need a full scan (if source changed significantly)
if [ -f "$LAST_SCAN" ] && [ $(find src/ -name "*.java" -newer "$LAST_SCAN" | wc -l) -lt 50 ]; then
echo "Performing incremental scan..."
sourceanalyzer -b $PROJECT -incremental -scan -f incremental.fpr
else
echo "Performing full scan..."
sourceanalyzer -b $PROJECT -clean
sourceanalyzer -b $PROJECT -scan -f full.fpr
fi
# Update timestamp
touch "$LAST_SCAN"

Conclusion

Fortify SCA provides comprehensive security analysis for Java applications:

  1. Deep Code Analysis: Identifies complex security vulnerabilities
  2. Customizable Rules: Extend with organization-specific security requirements
  3. CI/CD Integration: Automated security testing in development pipelines
  4. Comprehensive Reporting: Detailed vulnerability analysis and tracking
  5. Remediation Guidance: Actionable fixes for identified issues

Key Success Factors:

  • Regular scanning throughout development lifecycle
  • Custom rules for organization-specific requirements
  • Developer education on secure coding practices
  • Integration with issue tracking systems
  • Continuous monitoring of security metrics

By implementing Fortify SCA effectively, organizations can significantly improve their application security posture and catch vulnerabilities early in the development process.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper