Introduction to GitHub Code Scanning
GitHub Code Scanning is a GitHub Advanced Security feature that helps identify security vulnerabilities and coding errors in your Java codebase. It uses CodeQL (GitHub's semantic code analysis engine) and third-party tools to analyze your code and detect potential issues.
System Architecture Overview
GitHub Code Scanning Pipeline ├── Trigger Events │ ├ - Push to main/develop │ ├ - Pull Requests │ ├ - Schedule (daily/weekly) │ └ - Manual Trigger ├── Analysis Tools │ ├ - CodeQL Analysis │ ├ - Third-party Scanners │ ├ - Custom Queries │ └ - Security Patterns ├── Scanning Process │ ├ - Code Extraction │ ├ - Database Creation │ ├ - Query Execution │ └ - Results Processing └── Results & Integration ├ - Security Alerts ├ - PR Comments ├ - SARIF Reports └ - Security Dashboard
Core Implementation
1. GitHub Actions Workflow Configuration
codeql-analysis.yml
name: "CodeQL Advanced Security"
on:
push:
branches: [ "main", "develop" ]
pull_request:
branches: [ "main", "develop" ]
schedule:
- cron: '30 1 * * 0' # Weekly on Sunday at 1:30 AM
jobs:
analyze:
name: Analyze Java Code
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'java' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for better analysis
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
queries: security-and-quality, security-extended
config-file: ./.github/codeql/codeql-config.yml
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: 'maven'
- name: Build with Maven (Autobuild)
run: |
mvn clean compile -DskipTests
# Build the code to enable CodeQL to analyze it
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"
output: codeql-results.sarif
upload-database: true
- name: Upload SARIF results
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: codeql-results.sarif
wait-for-processing: true
advanced-security-scan.yml
name: "Advanced Security Scanning" on: push: branches: [ "main", "develop", "security/**" ] pull_request: branches: [ "main", "develop" ] workflow_dispatch: # Manual trigger jobs: security-scan: name: Comprehensive Security Scan runs-on: ubuntu-latest permissions: security-events: write actions: read contents: read steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up JDK 17 uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' cache: 'maven' # CodeQL Analysis - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: languages: java queries: +security-and-quality, security-extended, ../codeql-queries/java/ql/src/Security/CWE config-file: ./.github/codeql/codeql-config.yml - name: Build project run: mvn clean compile -DskipTests -DskipITs - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 with: category: "java-security" # Dependency Vulnerability Scanning - name: Run OWASP Dependency Check run: | mvn org.owasp:dependency-check-maven:check -DskipTests continue-on-error: true # Don't fail build on vulnerabilities - name: Upload Dependency Check Report uses: actions/upload-artifact@v4 with: name: dependency-check-report path: target/dependency-check-report.html retention-days: 30 # SpotBugs Security Analysis - name: Run SpotBugs Security Analysis run: | mvn com.github.spotbugs:spotbugs-maven-plugin:spotbugs -Dspotbugs.includeTests=false continue-on-error: true - name: Upload SpotBugs Report uses: actions/upload-artifact@v4 with: name: spotbugs-report path: target/spotbugs.xml retention-days: 30 # PMD Security Analysis - name: Run PMD Security Analysis run: | mvn pmd:pmd -Dpmd.includeTests=false continue-on-error: true - name: Upload PMD Report uses: actions/upload-artifact@v4 with: name: pmd-report path: target/pmd.xml retention-days: 30 # Custom Security Tools - name: Run Semgrep Security Scan uses: returntocorp/semgrep-action@v1 with: config: p/java-security output: semgrep-results.sarif - name: Upload Semgrep Results uses: github/codeql-action/upload-sarif@v3 with: sarif_file: semgrep-results.sarif
2. CodeQL Configuration
codeql-config.yml
# .github/codeql/codeql-config.yml name: "Custom Java Security Configuration" paths: - src/main/java - src/main/resources paths-ignore: - src/test - src/main/resources/static - target - .github - .mvn - '**/generated/**' - '**/test/**' queries: - uses: security-and-quality - uses: security-extended - name: custom-java-security-queries uses: ./.github/codeql/custom-queries - name: find-sql-injections uses: ./.github/codeql/java-sql-queries query-filters: - exclude: id: java/example/too-many-loops # Example of excluding specific queries packs: - codeql/java-queries libraries: - maven build-mode: automatic disable-default-queries: false query-timeout: 500 memory: 6144 threads: 0
Custom CodeQL Queries
// .github/codeql/custom-queries/java-security.ql
import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.security.QueryInjection
/**
* @name Potential SQL injection with JDBCTemplate
* @description Detects potential SQL injection vulnerabilities when using Spring JDBCTemplate
* @kind path-problem
* @problem.severity warning
* @security-severity 8.5
* @precision medium
* @id java/sql-injection-jdbctemplate
* @tags security
* external/cwe/cwe-089
*/
module JDBCTemplateInjection implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(MethodAccess ma |
ma.getMethod().hasName("query") and
ma.getMethod().getDeclaringType().hasQualifiedName("org.springframework.jdbc.core", "JdbcTemplate") and
source.asExpr() = ma.getArgument(0)
)
}
predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma |
ma.getMethod().hasName("execute") and
ma.getMethod().getDeclaringType().hasQualifiedName("java.sql", "Statement") and
sink.asExpr() = ma.getArgument(0)
)
}
}
module JDBCTemplateInjectionFlow = TaintTracking::Global<JDBCTemplateInjection>;
from JDBCTemplateInjectionFlow::PathNode source, JDBCTemplateInjectionFlow::PathNode sink
where JDBCTemplateInjectionFlow::flow(source, sink)
select sink, source, sink,
"Potential SQL injection vulnerability in JDBCTemplate query execution"
3. Maven Security Configuration
pom.xml Security Plugins
<properties>
<owasp.dependency.check.version>8.4.2</owasp.dependency.check.version>
<spotbugs.version>4.7.3</spotbugs.version>
<pmd.version>6.55.0</pmd.version>
<jacoco.version>0.8.8</jacoco.version>
<codeql.maven.plugin.version>1.0.0</codeql.maven.plugin.version>
</properties>
<build>
<plugins>
<!-- OWASP Dependency Check -->
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>${owasp.dependency.check.version}</version>
<configuration>
<format>HTML</format>
<format>XML</format>
<failBuildOnAnyVulnerability>true</failBuildOnAnyVulnerability>
<failOnCVSS>7</failOnCVSS>
<skipTestScope>true</skipTestScope>
<skipRuntimeScope>false</skipRuntimeScope>
<skipProvidedScope>false</skipProvidedScope>
<skipSystemScope>false</skipSystemScope>
<suppressionFiles>
<suppressionFile>${project.basedir}/security/dependency-check-suppressions.xml</suppressionFile>
</suppressionFiles>
<analyzer>
<assemblyEnabled>false</assemblyEnabled>
</analyzer>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- SpotBugs Security Configuration -->
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>${spotbugs.version}</version>
<configuration>
<effort>Max</effort>
<threshold>Low</threshold>
<failOnError>true</failOnError>
<excludeFilterFile>${project.basedir}/security/spotbugs-security-exclude.xml</excludeFilterFile>
<plugins>
<plugin>
<groupId>com.h3x3lf1t</groupId>
<artifactId>findsecbugs</artifactId>
<version>1.12.0</version>
</plugin>
</plugins>
</configuration>
<executions>
<execution>
<id>spotbugs-security-scan</id>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- PMD Security Rules -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>${pmd.version}</version>
<configuration>
<rulesets>
<ruleset>${project.basedir}/security/pmd-security-ruleset.xml</ruleset>
<ruleset>category/java/security.xml</ruleset>
</rulesets>
<failOnViolation>true</failOnViolation>
<printFailingErrors>true</printFailingErrors>
<linkXRef>false</linkXRef>
</configuration>
<executions>
<execution>
<id>pmd-security-scan</id>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- JaCoCo Code Coverage -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>INSTRUCTION</counter>
<value>COVEREDRATIO</value>
<minimum>0.80</minimum>
</limit>
<limit>
<counter>BRANCH</counter>
<value>COVEREDRATIO</value>
<minimum>0.70</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>security-scan</id>
<build>
<plugins>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<executions>
<execution>
<id>dependency-check</id>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
4. Security Configuration Files
OWASP Dependency Check Suppressions
<?xml version="1.0" encoding="UTF-8"?> <!-- security/dependency-check-suppressions.xml --> <suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd"> <!-- Suppress false positives for specific libraries --> <suppress> <notes><![CDATA[ False positive for logback-core - see https://github.com/jeremylong/DependencyCheck/issues/XXXX ]]></notes> <packageUrl regex="true">^pkg:maven/ch\.qos\.logback/logback\-core@.*$</packageUrl> <cve>CVE-2023-12345</cve> </suppress> <!-- Suppress specific CVE in test scope --> <suppress> <notes><![CDATA[ Suppress test dependencies with known vulnerabilities ]]></notes> <gav regex="true">^junit:junit:.*$</gav> <cve>CVE-2020-15250</cve> </suppress> <!-- Suppress vulnerabilities with CVSS score below threshold --> <suppress base="true"> <notes><![CDATA[ Suppress vulnerabilities with CVSS score less than 7.0 ]]></notes> <cvssBelow>7.0</cvssBelow> </suppress> </suppressions>
SpotBugs Security Exclusions
<!-- security/spotbugs-security-exclude.xml --> <FindBugsFilter> <Match> <!-- Exclude specific security patterns in test code --> <Class name="~.*Test.*"/> <Bug pattern="HARD_CODE_PASSWORD"/> </Match> <Match> <!-- Exclude intentional password fields in configuration --> <Class name="com.example.config.SecurityConfig"/> <Field name="defaultPassword"/> <Bug pattern="HARD_CODE_PASSWORD"/> </Match> <Match> <!-- Exclude specific false positives for serialization --> <Class name="~.*\.model\..*"/> <Bug pattern="SE_BAD_FIELD"/> </Match> <Match> <!-- Exclude specific cryptographic patterns --> <Class name="com.example.security.CryptoUtil"/> <Bug pattern="STATIC_IV"/> </Match> </FindBugsFilter>
PMD Security Ruleset
<?xml version="1.0"?> <!-- security/pmd-security-ruleset.xml --> <ruleset name="Security Rules" xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"> <description>Custom security rules for Java applications</description> <!-- Import standard security rules --> <rule ref="category/java/security.xml"> <exclude name="InsecureCryptoIv"/> </rule> <!-- Custom security rules --> <rule ref="category/java/bestpractices.xml/AvoidUsingHardCodedIP"/> <rule ref="category/java/errorprone.xml/BeanMembersShouldSerialize"/> <!-- Custom rule configuration --> <rule ref="category/java/security.xml/HardCodedCryptoKey"> <properties> <property name="minimumLength" value="8"/> </properties> </rule> </ruleset>
5. Secure Java Code Examples
Secure Authentication Service
/**
* Secure authentication service implementation with security best practices.
*
* @author Security Team
* @version 1.0
*/
package com.example.security;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Value;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.Base64;
/**
* Secure authentication service with protection against common vulnerabilities.
*/
@Service
public class SecureAuthService {
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
private static final int ITERATIONS = 10000;
private static final int KEY_LENGTH = 256;
private static final int SALT_LENGTH = 32;
@Value("${security.pepper:defaultPepperValue}")
private String pepper;
private final PasswordEncoder passwordEncoder;
public SecureAuthService() {
this.passwordEncoder = new BCryptPasswordEncoder(12); // Strong work factor
}
/**
* Securely hash password with salt and pepper.
*
* @param password the plain text password
* @return hashed password with salt
*/
public String hashPassword(String password) {
if (password == null || password.trim().isEmpty()) {
throw new IllegalArgumentException("Password cannot be null or empty");
}
// Generate cryptographically secure salt
byte[] salt = generateSalt();
// Combine password with pepper
String pepperedPassword = password + pepper;
// Hash using PBKDF2
byte[] hash = pbkdf2Hash(pepperedPassword.toCharArray(), salt);
// Encode salt and hash together
return encodeSaltAndHash(salt, hash);
}
/**
* Verify password against stored hash.
*
* @param password the plain text password
* @param storedHash the stored hash with salt
* @return true if password matches
*/
public boolean verifyPassword(String password, String storedHash) {
if (password == null || storedHash == null) {
return false;
}
try {
// Decode salt and hash from stored value
String[] parts = storedHash.split(":");
if (parts.length != 2) {
return false;
}
byte[] salt = Base64.getDecoder().decode(parts[0]);
byte[] expectedHash = Base64.getDecoder().decode(parts[1]);
// Combine password with pepper
String pepperedPassword = password + pepper;
// Compute hash for provided password
byte[] testHash = pbkdf2Hash(pepperedPassword.toCharArray(), salt);
// Constant-time comparison to prevent timing attacks
return constantTimeEquals(expectedHash, testHash);
} catch (IllegalArgumentException | ArrayIndexOutOfBoundsException e) {
return false;
}
}
/**
* Generate cryptographically secure salt.
*
* @return secure random salt
*/
private byte[] generateSalt() {
byte[] salt = new byte[SALT_LENGTH];
SECURE_RANDOM.nextBytes(salt);
return salt;
}
/**
* PBKDF2 password hashing.
*
* @param password the password as char array
* @param salt the salt
* @return hashed password
*/
private byte[] pbkdf2Hash(char[] password, byte[] salt) {
try {
PBEKeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, KEY_LENGTH);
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
return skf.generateSecret(spec).getEncoded();
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw new SecurityException("Password hashing failed", e);
} finally {
// Clear the password array
Arrays.fill(password, '\0');
}
}
/**
* Encode salt and hash for storage.
*
* @param salt the salt
* @param hash the hash
* @return encoded string
*/
private String encodeSaltAndHash(byte[] salt, byte[] hash) {
String encodedSalt = Base64.getEncoder().encodeToString(salt);
String encodedHash = Base64.getEncoder().encodeToString(hash);
return encodedSalt + ":" + encodedHash;
}
/**
* Constant-time array comparison to prevent timing attacks.
*
* @param a first array
* @param b second array
* @return true if arrays are equal
*/
private boolean constantTimeEquals(byte[] a, byte[] b) {
if (a.length != b.length) {
return false;
}
int result = 0;
for (int i = 0; i < a.length; i++) {
result |= a[i] ^ b[i];
}
return result == 0;
}
/**
* Generate secure random token for CSRF protection.
*
* @return secure random token
*/
public String generateCsrfToken() {
byte[] tokenBytes = new byte[32];
SECURE_RANDOM.nextBytes(tokenBytes);
return Base64.getUrlEncoder().withoutPadding().encodeToString(tokenBytes);
}
/**
* Validate CSRF token.
*
* @param expected the expected token
* @param actual the actual token
* @return true if tokens match
*/
public boolean validateCsrfToken(String expected, String actual) {
if (expected == null || actual == null) {
return false;
}
return constantTimeEquals(
expected.getBytes(java.nio.charset.StandardCharsets.UTF_8),
actual.getBytes(java.nio.charset.StandardCharsets.UTF_8)
);
}
}
SQL Injection Protected Repository
/**
* Secure repository implementation with SQL injection protection.
*/
@Repository
public class SecureOrderRepository {
private final JdbcTemplate jdbcTemplate;
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public SecureOrderRepository(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
/**
* Find orders by customer ID using parameterized query.
*
* @param customerId the customer ID
* @return list of orders
*/
public List<Order> findByCustomerId(String customerId) {
String sql = "SELECT id, customer_id, amount, status, created_at " +
"FROM orders WHERE customer_id = ? AND status = 'ACTIVE'";
return jdbcTemplate.query(sql, new Object[]{customerId}, (rs, rowNum) ->
new Order(
rs.getString("id"),
rs.getString("customer_id"),
rs.getBigDecimal("amount"),
rs.getString("status"),
rs.getTimestamp("created_at").toLocalDateTime()
)
);
}
/**
* Search orders with multiple parameters safely.
*
* @param criteria search criteria
* @return list of orders
*/
public List<Order> searchOrders(OrderSearchCriteria criteria) {
String sql = "SELECT id, customer_id, amount, status, created_at " +
"FROM orders WHERE 1=1";
MapSqlParameterSource params = new MapSqlParameterSource();
if (criteria.getCustomerId() != null) {
sql += " AND customer_id = :customerId";
params.addValue("customerId", criteria.getCustomerId());
}
if (criteria.getMinAmount() != null) {
sql += " AND amount >= :minAmount";
params.addValue("minAmount", criteria.getMinAmount());
}
if (criteria.getMaxAmount() != null) {
sql += " AND amount <= :maxAmount";
params.addValue("maxAmount", criteria.getMaxAmount());
}
if (criteria.getStatus() != null) {
sql += " AND status = :status";
params.addValue("status", criteria.getStatus());
}
sql += " ORDER BY created_at DESC";
return namedParameterJdbcTemplate.query(sql, params, (rs, rowNum) ->
new Order(
rs.getString("id"),
rs.getString("customer_id"),
rs.getBigDecimal("amount"),
rs.getString("status"),
rs.getTimestamp("created_at").toLocalDateTime()
)
);
}
/**
* Batch insert orders safely.
*
* @param orders list of orders to insert
*/
public void batchInsert(List<Order> orders) {
String sql = "INSERT INTO orders (id, customer_id, amount, status, created_at) " +
"VALUES (?, ?, ?, ?, ?)";
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
Order order = orders.get(i);
ps.setString(1, order.getId());
ps.setString(2, order.getCustomerId());
ps.setBigDecimal(3, order.getAmount());
ps.setString(4, order.getStatus());
ps.setTimestamp(5, Timestamp.valueOf(order.getCreatedAt()));
}
@Override
public int getBatchSize() {
return orders.size();
}
});
}
/**
* UNSAFE METHOD - Demonstrates SQL injection vulnerability.
* This method would be caught by CodeQL analysis.
*
* @deprecated This method is vulnerable to SQL injection
*/
@Deprecated
public List<Order> unsafeFindByCustomerId(String customerId) {
// UNSAFE: String concatenation in SQL query
String sql = "SELECT * FROM orders WHERE customer_id = '" + customerId + "'";
return jdbcTemplate.query(sql, (rs, rowNum) ->
new Order(
rs.getString("id"),
rs.getString("customer_id"),
rs.getBigDecimal("amount"),
rs.getString("status"),
rs.getTimestamp("created_at").toLocalDateTime()
)
);
}
}
6. Security Headers Configuration
Security Configuration Class
/**
* Security configuration for HTTP headers and protection.
*/
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Value("${security.csp.policy:default-src 'self'}")
private String cspPolicy;
/**
* Main security configuration.
*/
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// CSRF protection
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringRequestMatchers("/api/public/**")
)
// Security headers
.headers(headers -> headers
.contentSecurityPolicy(csp -> csp.policyDirectives(cspPolicy))
.frameOptions(frame -> frame.deny())
.xssProtection(xss -> xss.block(true))
.contentTypeOptions(contentType -> {})
)
// Session management
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
)
// Authorization
.authorizeHttpRequests(authz -> authz
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/**").authenticated()
.anyRequest().permitAll()
)
// Authentication
.formLogin(form -> form
.loginPage("/login")
.permitAll()
)
.logout(logout -> logout
.logoutSuccessUrl("/")
.permitAll()
);
return http.build();
}
/**
* Password encoder with strong hashing.
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12);
}
/**
* Custom security headers filter.
*/
@Bean
public FilterRegistrationBean<SecurityHeadersFilter> securityHeadersFilter() {
FilterRegistrationBean<SecurityHeadersFilter> registrationBean =
new FilterRegistrationBean<>();
registrationBean.setFilter(new SecurityHeadersFilter());
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
}
/**
* Custom security headers filter.
*/
@Component
public class SecurityHeadersFilter implements Filter {
@Value("${security.headers.csp:default-src 'self'}")
private String cspPolicy;
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
// Security headers
httpResponse.setHeader("Content-Security-Policy", cspPolicy);
httpResponse.setHeader("X-Content-Type-Options", "nosniff");
httpResponse.setHeader("X-Frame-Options", "DENY");
httpResponse.setHeader("X-XSS-Protection", "1; mode=block");
httpResponse.setHeader("Strict-Transport-Security",
"max-age=31536000; includeSubDomains");
httpResponse.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
httpResponse.setHeader("Permissions-Policy",
"geolocation=(), microphone=(), camera=()");
chain.doFilter(request, response);
}
}
7. GitHub Security Dashboard Configuration
security.md
# Security Policy ## Supported Versions | Version | Supported | | ------- | ------------------ | | 1.0.x | :white_check_mark: | | < 1.0 | :x: | ## Reporting a Vulnerability We take the security of our software seriously. If you believe you've found a security vulnerability, please report it to us. ### Private Reporting Please **DO NOT** disclose security-related issues publicly until we've had a chance to address them. 1. **Email**: [email protected] 2. **Encryption**: Use our [PGP key](https://example.com/security/pgp-key.asc) 3. **Response Time**: We aim to respond within 48 hours ### Public Reporting For non-sensitive security issues, you can: 1. Create a GitHub issue 2. Use the security advisory feature ## Security Scanning Our project uses automated security scanning: ### Tools Used - **GitHub CodeQL**: Static analysis for security vulnerabilities - **OWASP Dependency Check**: Dependency vulnerability scanning - **SpotBugs**: Bug patterns including security issues - **PMD**: Static analysis for code quality ### Scan Frequency - **On every push**: CodeQL analysis - **Daily**: Full security scan - **On release**: Comprehensive security audit ## Security Practices ### Code Security - All code undergoes security review - Automated security testing in CI/CD - Regular dependency updates - Security headers enforcement ### Data Protection - Encryption at rest and in transit - Secure password hashing (PBKDF2/BCrypt) - Principle of least privilege - Regular security audits ## Dependency Security We monitor dependencies for vulnerabilities: ### Automatic Updates - Dependabot security updates enabled - Weekly dependency scanning - Manual review of major updates ### Vulnerability Response 1. **Critical**: Fix within 24 hours 2. **High**: Fix within 72 hours 3. **Medium**: Fix within 1 week 4. **Low**: Fix in next release cycle
8. Advanced Security Workflows
scheduled-security-scan.yml
name: "Scheduled Security Deep Scan"
on:
schedule:
- cron: '0 2 * * 1' # Every Monday at 2 AM
workflow_dispatch: # Manual trigger
jobs:
deep-security-scan:
name: Deep Security Analysis
runs-on: ubuntu-latest
timeout-minutes: 120
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: 'maven'
# Comprehensive CodeQL Analysis
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: java
queries: security-and-quality, security-extended, ../codeql-queries/java/ql/src/Security/CWE
build-mode: manual
- name: Build with detailed compilation
run: |
mvn clean compile -DskipTests -Dmaven.compiler.debug=true -Dmaven.compiler.debuglevel=lines,vars,source
- name: Advanced CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
output: advanced-codeql-results.sarif
upload-database: true
# SAST Tools Suite
- name: Run SonarQube Analysis
uses: SonarSource/sonarcloud-github-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- name: Run Snyk Security Scan
uses: snyk/actions/maven@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
# Dependency Analysis
- name: Run OWASP Dependency Check
run: mvn org.owasp:dependency-check-maven:aggregate -DskipTests
- name: Run License Compliance Check
run: mvn org.codehaus.mojo:license-maven-plugin:aggregate-add-third-party
# Security Report Generation
- name: Generate Security Report
run: |
echo "# Security Scan Report - $(date)" > security-report.md
echo "## Summary" >> security-report.md
echo "- CodeQL Issues: TODO" >> security-report.md
echo "- Dependency Vulnerabilities: TODO" >> security-report.md
echo "- License Compliance: TODO" >> security-report.md
echo "## Detailed Findings" >> security-report.md
- name: Upload Security Reports
uses: actions/upload-artifact@v4
with:
name: security-reports-$(date +%Y%m%d)
path: |
target/dependency-check-report.html
security-report.md
target/site/
retention-days: 90
- name: Notify Security Team
if: failure()
uses: 8398a7/action-slack@v3
with:
status: failure
text: Security scan failed. Please review the findings.
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_SECURITY_WEBHOOK }}
9. Security Testing
Security Test Suite
/**
* Security test suite for vulnerability detection.
*/
@SpringBootTest
class SecurityTestSuite {
@Autowired
private SecureAuthService authService;
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@BeforeEach
void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
/**
* Test for SQL injection vulnerabilities.
*/
@Test
void testSqlInjectionProtection() throws Exception {
String sqlInjectionPayload = "admin' OR '1'='1";
mockMvc.perform(get("/api/orders")
.param("customerId", sqlInjectionPayload))
.andExpect(status().isBadRequest()) // Should reject malicious input
.andExpect(content().string(not(containsString("admin"))));
}
/**
* Test for XSS protection.
*/
@Test
void testXssProtection() throws Exception {
String xssPayload = "<script>alert('XSS')</script>";
mockMvc.perform(post("/api/orders")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"customerId\": \"" + xssPayload + "\"}"))
.andExpect(header().string("X-XSS-Protection", "1; mode=block"))
.andExpect(content().string(not(containsString("<script>"))));
}
/**
* Test CSRF protection.
*/
@Test
void testCsrfProtection() throws Exception {
mockMvc.perform(post("/api/orders")
.contentType(MediaType.APPLICATION_JSON)
.content("{}"))
.andExpect(status().isForbidden()); // Should require CSRF token
}
/**
* Test secure password hashing.
*/
@Test
void testPasswordHashingSecurity() {
String password = "MySecurePassword123!";
String hash1 = authService.hashPassword(password);
String hash2 = authService.hashPassword(password);
// Hashes should be different due to random salt
assertNotEquals(hash1, hash2);
// But both should verify correctly
assertTrue(authService.verifyPassword(password, hash1));
assertTrue(authService.verifyPassword(password, hash2));
// Wrong password should not verify
assertFalse(authService.verifyPassword("WrongPassword", hash1));
}
/**
* Test timing attack protection.
*/
@Test
void testTimingAttackProtection() {
String password = "testPassword";
String hash = authService.hashPassword(password);
// Measure verification time for correct and incorrect passwords
long correctTime = measureVerificationTime(password, hash);
long incorrectTime = measureVerificationTime("wrongPassword", hash);
// Times should be similar (within reasonable margin)
long timeDifference = Math.abs(correctTime - incorrectTime);
assertTrue(timeDifference < 100,
"Verification times should not differ significantly to prevent timing attacks");
}
private long measureVerificationTime(String password, String hash) {
long startTime = System.nanoTime();
authService.verifyPassword(password, hash);
return System.nanoTime() - startTime;
}
/**
* Test security headers.
*/
@Test
void testSecurityHeaders() throws Exception {
mockMvc.perform(get("/api/public/health"))
.andExpect(header().string("X-Content-Type-Options", "nosniff"))
.andExpect(header().string("X-Frame-Options", "DENY"))
.andExpect(header().string("X-XSS-Protection", "1; mode=block"))
.andExpect(header().exists("Strict-Transport-Security"));
}
}
Best Practices
1. Security-First Development
// Secure by default - always validate input
public class InputValidator {
public static String sanitizeInput(String input) {
if (input == null) return null;
// Remove potential XSS vectors
return input.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll("\"", """)
.replaceAll("'", "'");
}
public static boolean isValidCustomerId(String customerId) {
return customerId != null &&
customerId.matches("[a-zA-Z0-9-]{1,50}"); // Strict pattern
}
}
2. Continuous Security Monitoring
# Security dashboard configuration security_metrics: code_quality: max_critical_issues: 0 max_high_issues: 5 max_medium_issues: 20 dependencies: max_critical_vulnerabilities: 0 max_high_vulnerabilities: 3 update_frequency: weekly coverage: min_security_test_coverage: 80 min_overall_coverage: 70
3. Incident Response
public class SecurityIncidentLogger {
private static final Logger SECURITY_LOGGER =
LoggerFactory.getLogger("SECURITY");
public static void logSecurityIncident(String event, String details) {
SECURITY_LOGGER.warn("SECURITY_INCIDENT: {} - {}", event, details);
// Additional security monitoring integration
notifySecurityTeam(event, details);
}
}
Conclusion
This comprehensive GitHub Code Scanning implementation provides:
- Automated security scanning with CodeQL and multiple tools
- Continuous vulnerability detection integrated into development workflow
- Security-focused code examples demonstrating best practices
- Comprehensive testing for security vulnerabilities
- Security monitoring and reporting with actionable insights
Key benefits:
- Early vulnerability detection before production deployment
- Security compliance through automated checks
- Developer education with immediate feedback
- Risk reduction through continuous monitoring
- Compliance reporting with detailed security metrics
The setup enables organizations to maintain high security standards while accelerating development through automated security controls and continuous monitoring.
Advanced Java Supply Chain Security, Kubernetes Hardening & Runtime Threat Detection
Sigstore Rekor in Java – https://macronepal.com/blog/sigstore-rekor-in-java/
Explains integrating Sigstore Rekor into Java systems to create a transparent, tamper-proof log of software signatures and metadata for verifying supply chain integrity.
Securing Java Applications with Chainguard Wolfi – https://macronepal.com/blog/securing-java-applications-with-chainguard-wolfi-a-comprehensive-guide/
Explains using Chainguard Wolfi minimal container images to reduce vulnerabilities and secure Java applications with hardened, lightweight runtime environments.
Cosign Image Signing in Java Complete Guide – https://macronepal.com/blog/cosign-image-signing-in-java-complete-guide/
Explains how to digitally sign container images using Cosign in Java-based workflows to ensure authenticity and prevent unauthorized modifications.
Secure Supply Chain Enforcement Kyverno Image Verification for Java Containers – https://macronepal.com/blog/secure-supply-chain-enforcement-kyverno-image-verification-for-java-containers/
Explains enforcing Kubernetes policies with Kyverno to verify container image signatures and ensure only trusted Java container images are deployed.
Pod Security Admission in Java Securing Kubernetes Deployments for JVM Applications – https://macronepal.com/blog/pod-security-admission-in-java-securing-kubernetes-deployments-for-jvm-applications/
Explains Kubernetes Pod Security Admission policies that enforce security rules like restricted privileges and safe configurations for Java workloads.
Securing Java Applications at Runtime Kubernetes Security Context – https://macronepal.com/blog/securing-java-applications-at-runtime-a-guide-to-kubernetes-security-context/
Explains how Kubernetes security contexts control runtime permissions, user IDs, and access rights for Java containers to improve isolation.
Process Anomaly Detection in Java Behavioral Monitoring – https://macronepal.com/blog/process-anomaly-detection-in-java-comprehensive-behavioral-monitoring-2/
Explains detecting abnormal runtime behavior in Java applications to identify potential security threats using process monitoring techniques.
Achieving Security Excellence CIS Benchmark Compliance for Java Applications – https://macronepal.com/blog/achieving-security-excellence-implementing-cis-benchmark-compliance-for-java-applications/
Explains applying CIS security benchmarks to Java environments to standardize hardening and improve overall system security posture.
Process Anomaly Detection in Java Behavioral Monitoring – https://macronepal.com/blog/process-anomaly-detection-in-java-comprehensive-behavioral-monitoring/
Explains behavioral monitoring of Java processes to detect anomalies and improve runtime security through continuous observation and analysis.
JAVA CODE COMPILER