diff --git a/docs/probes.md b/docs/probes.md index 58332f306ac..85878020809 100644 --- a/docs/probes.md +++ b/docs/probes.md @@ -657,7 +657,7 @@ The probe returns 1 true outcome if the project has no workflows "write" permiss **Motivation**: Memory safety in software should be considered a continuum, rather than being binary. While some languages and tools are memory safe by default, it may still be possible, and sometimes unavoidable, to write unsafe code in them. Unsafe code allow developers to bypass normal safety checks and directly manipulate memory. -**Implementation**: The probe is ecosystem-specific and will surface non memory safe practices in the project by identifying unsafe code blocks. Unsafe code blocks are supported in rust, go, c#, and swift, but only go and c# are supported by this probe at this time: - for go the probe will look for the use of the `unsafe` include directive. - for c# the probe will look at the csproj and identify the use of the `AllowUnsafeBlocks` property. +**Implementation**: The probe is ecosystem-specific and will surface non memory safe practices in the project by identifying unsafe code blocks. Unsafe code blocks are supported in rust, go, c#, Java, and swift, but only go, c# and Java are supported by this probe at this time: - for go the probe will look for the use of the `unsafe` include directive. - for c# the probe will look at the csproj and identify the use of the `AllowUnsafeBlocks` property. - for Java the probe will look at references to either the `sun.misc.Unsafe` class or the `jdk.internal.misc.Unsafe` class. **Outcomes**: For supported ecosystem, the probe returns OutcomeTrue per unsafe block. If the project has no unsafe blocks, the probe returns OutcomeFalse. diff --git a/probes/unsafeblock/def.yml b/probes/unsafeblock/def.yml index 3feebeba185..aef5df94220 100644 --- a/probes/unsafeblock/def.yml +++ b/probes/unsafeblock/def.yml @@ -21,9 +21,10 @@ motivation: > Unsafe code allow developers to bypass normal safety checks and directly manipulate memory. implementation: > The probe is ecosystem-specific and will surface non memory safe practices in the project by identifying unsafe code blocks. - Unsafe code blocks are supported in rust, go, c#, and swift, but only go and c# are supported by this probe at this time: + Unsafe code blocks are supported in rust, go, c#, Java, and swift, but only go, c# and Java are supported by this probe at this time: - for go the probe will look for the use of the `unsafe` include directive. - for c# the probe will look at the csproj and identify the use of the `AllowUnsafeBlocks` property. + - for Java the probe will look at references to either the `sun.misc.Unsafe` class or the `jdk.internal.misc.Unsafe` class. outcome: - For supported ecosystem, the probe returns OutcomeTrue per unsafe block. - If the project has no unsafe blocks, the probe returns OutcomeFalse. @@ -38,6 +39,7 @@ ecosystem: languages: - go - c# + - java clients: - github - gitlab diff --git a/probes/unsafeblock/impl.go b/probes/unsafeblock/impl.go index f40c09cca21..b74b4db1828 100644 --- a/probes/unsafeblock/impl.go +++ b/probes/unsafeblock/impl.go @@ -15,11 +15,13 @@ package unsafeblock import ( + "bytes" "embed" "fmt" "go/parser" "go/token" "reflect" + "regexp" "strings" "github.com/ossf/scorecard/v5/checker" @@ -52,6 +54,11 @@ var languageMemorySafeSpecs = map[clients.LanguageName]languageMemoryCheckConfig funcPointer: checkDotnetAllowUnsafeBlocks, Desc: "Check if C# code uses unsafe blocks", }, + + clients.Java: { + funcPointer: checkJavaUnsafeClass, + Desc: "Check if Java code uses the Unsafe class", + }, } func init() { @@ -208,3 +215,119 @@ func csProjAllosUnsafeBlocks(path string, content []byte, args ...interface{}) ( return true, nil } + +// Java + +var ( + // javaMultiLineCommentRe matches /* ... */ comments (including across newlines). + javaMultiLineCommentRe = regexp.MustCompile(`(?s)/\*.*?\*/`) + // javaSingleLineCommentRe matches // ... to end of line. + javaSingleLineCommentRe = regexp.MustCompile(`//[^\n]*`) + // javaMultiLineStringRe matches multi-line string literals. + javaMultiLineStringRe = regexp.MustCompile(`(?s)""".*?"""`) + // javaSingleLineStringRe matches single-line string literals. + javaSingleLineStringRe = regexp.MustCompile(`"(?:[^"\\]|\\.)*"`) + + // javaUnsafeImportRe matches import statements for sun.misc.Unsafe or + // jdk.internal.misc.Unsafe, allowing optional whitespace between tokens + // (e.g. "import sun . misc . Unsafe ;"). + javaUnsafeImportRe = regexp.MustCompile( + `\bimport\s+(?:sun\s*\.\s*misc|jdk\s*\.\s*internal\s*\.\s*misc)\s*\.\s*Unsafe\s*;`) + + // javaUnsafeFQNRe matches fully-qualified references to sun.misc.Unsafe or + // jdk.internal.misc.Unsafe in code (including inside import statements). + javaUnsafeFQNRe = regexp.MustCompile( + `\b(?:sun\s*\.\s*misc|jdk\s*\.\s*internal\s*\.\s*misc)\s*\.\s*Unsafe\b`) +) + +// stripJavaComments removes single-line and multi-line comments from Java +// source, preserving newlines so that line numbers remain accurate. +func stripJavaComments(content []byte) []byte { + // Replace multi-line comments with an equal number of newlines. + src := javaMultiLineCommentRe.ReplaceAllFunc(content, func(match []byte) []byte { + return bytes.Repeat([]byte("\n"), bytes.Count(match, []byte("\n"))) + }) + // Remove single-line comments (the newline itself is not part of the match). + return javaSingleLineCommentRe.ReplaceAll(src, nil) +} + +// stripJavaStringLiterals removes single-line and multi-line string literals from Java +// source, preserving newlines so that line numbers remain accurate. +func stripJavaStringLiterals(content []byte) []byte { + // Replace multi-line string literals with an equal number of newlines. + src := javaMultiLineStringRe.ReplaceAllFunc(content, func(match []byte) []byte { + return bytes.Repeat([]byte("\n"), bytes.Count(match, []byte("\n"))) + }) + // Remove single-line string literals (the newline itself is not part of the match). + return javaSingleLineStringRe.ReplaceAll(src, nil) +} + +// javaLineNumber returns the 1-based line number of the byte at offset within src. +func javaLineNumber(src []byte, offset int) uint { + return uint(bytes.Count(src[:offset], []byte("\n")) + 1) +} + +func checkJavaUnsafeClass(client *checker.CheckRequest) ([]finding.Finding, error) { + findings := []finding.Finding{} + if err := fileparser.OnMatchingFileContentDo(client.RepoClient, fileparser.PathMatcher{ + Pattern: "*.java", + CaseSensitive: false, + }, javaCodeUsesUnsafeClass, &findings); err != nil { + return nil, err + } + + return findings, nil +} + +func javaCodeUsesUnsafeClass(path string, content []byte, args ...interface{}) (bool, error) { + findings, ok := args[0].(*[]finding.Finding) + if !ok { + // panic if it is not correct type + panic(fmt.Sprintf("expected type findings, got %v", reflect.TypeOf(args[0]))) + } + + src := stripJavaStringLiterals(stripJavaComments(content)) + + // Report each import of an Unsafe class. + importLocs := javaUnsafeImportRe.FindAllIndex(src, -1) + for _, loc := range importLocs { + line := javaLineNumber(src, loc[0]) + found, err := finding.NewWith(fs, Probe, + "Java code imports the Unsafe class", &finding.Location{ + Path: path, LineStart: &line, + }, finding.OutcomeTrue) + if err != nil { + return false, fmt.Errorf("create finding: %w", err) + } + *findings = append(*findings, *found) + } + + // Report fully-qualified usages of an Unsafe class that are not part of + // an import statement (i.e. direct usage without a prior import). + for _, loc := range javaUnsafeFQNRe.FindAllIndex(src, -1) { + if withinAnyRange(loc[0], importLocs) { + continue + } + line := javaLineNumber(src, loc[0]) + found, err := finding.NewWith(fs, Probe, + "Java code uses the Unsafe class", &finding.Location{ + Path: path, LineStart: &line, + }, finding.OutcomeTrue) + if err != nil { + return false, fmt.Errorf("create finding: %w", err) + } + *findings = append(*findings, *found) + } + + return true, nil +} + +// withinAnyRange reports whether offset falls inside any of the given [start, end) ranges. +func withinAnyRange(offset int, ranges [][]int) bool { + for _, r := range ranges { + if offset >= r[0] && offset < r[1] { + return true + } + } + return false +} diff --git a/probes/unsafeblock/impl_test.go b/probes/unsafeblock/impl_test.go index b7dcb272dc8..e8e2d08a202 100644 --- a/probes/unsafeblock/impl_test.go +++ b/probes/unsafeblock/impl_test.go @@ -318,6 +318,251 @@ func Test_Run(t *testing.T) { }, err: nil, }, + // Java + { + name: "Java - no files", + repoLanguages: []clients.Language{ + {Name: clients.Java, NumLines: 0}, + }, + filenames: []string{}, + expected: []finding.Finding{ + { + Probe: Probe, + Message: "All supported ecosystems do not declare or use unsafe code blocks", + Outcome: finding.OutcomeFalse, + }, + }, + err: nil, + }, + { + name: "Java - safe no imports", + repoLanguages: []clients.Language{ + {Name: clients.Java, NumLines: 0}, + }, + filenames: []string{ + "safe-no-imports.java", + }, + expected: []finding.Finding{ + { + Probe: Probe, + Message: "All supported ecosystems do not declare or use unsafe code blocks", + Outcome: finding.OutcomeFalse, + }, + }, + err: nil, + }, + { + name: "Java - safe with imports", + repoLanguages: []clients.Language{ + {Name: clients.Java, NumLines: 0}, + }, + filenames: []string{ + "safe-with-imports.java", + }, + expected: []finding.Finding{ + { + Probe: Probe, + Message: "All supported ecosystems do not declare or use unsafe code blocks", + Outcome: finding.OutcomeFalse, + }, + }, + err: nil, + }, + { + name: "Java - unsafe in sun.misc with imports", + repoLanguages: []clients.Language{ + {Name: clients.Java, NumLines: 0}, + }, + filenames: []string{ + "unsafe-sun-with-imports.java", + }, + expected: []finding.Finding{ + { + Probe: Probe, + Message: "Java code imports the Unsafe class", + Outcome: finding.OutcomeTrue, + Remediation: &finding.Remediation{ + Text: "Visit the OpenSSF Memory Safety SIG guidance on how to make your project memory safe.\nGuidance for [Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-memory-safe-by-default-languages.md)\nGuidance for [Non Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-non-memory-safe-by-default-languages.md)", + Effort: 2, + }, + Location: &finding.Location{Path: "unsafe-sun-with-imports.java", LineStart: toUintPointer(3)}, + }, + }, + err: nil, + }, + { + name: "Java - unsafe in sun.misc without imports", + repoLanguages: []clients.Language{ + {Name: clients.Java, NumLines: 0}, + }, + filenames: []string{ + "unsafe-sun-without-imports.java", + }, + expected: []finding.Finding{ + { + Probe: Probe, + Message: "Java code uses the Unsafe class", + Outcome: finding.OutcomeTrue, + Remediation: &finding.Remediation{ + Text: "Visit the OpenSSF Memory Safety SIG guidance on how to make your project memory safe.\nGuidance for [Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-memory-safe-by-default-languages.md)\nGuidance for [Non Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-non-memory-safe-by-default-languages.md)", + Effort: 2, + }, + Location: &finding.Location{Path: "unsafe-sun-without-imports.java", LineStart: toUintPointer(21)}, + }, + { + Probe: Probe, + Message: "Java code uses the Unsafe class", + Outcome: finding.OutcomeTrue, + Remediation: &finding.Remediation{ + Text: "Visit the OpenSSF Memory Safety SIG guidance on how to make your project memory safe.\nGuidance for [Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-memory-safe-by-default-languages.md)\nGuidance for [Non Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-non-memory-safe-by-default-languages.md)", + Effort: 2, + }, + Location: &finding.Location{Path: "unsafe-sun-without-imports.java", LineStart: toUintPointer(22)}, + }, + { + Probe: Probe, + Message: "Java code uses the Unsafe class", + Outcome: finding.OutcomeTrue, + Remediation: &finding.Remediation{ + Text: "Visit the OpenSSF Memory Safety SIG guidance on how to make your project memory safe.\nGuidance for [Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-memory-safe-by-default-languages.md)\nGuidance for [Non Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-non-memory-safe-by-default-languages.md)", + Effort: 2, + }, + Location: &finding.Location{Path: "unsafe-sun-without-imports.java", LineStart: toUintPointer(24)}, + }, + }, + err: nil, + }, + { + name: "Java - unsafe in jdk.internal.misc with imports", + repoLanguages: []clients.Language{ + {Name: clients.Java, NumLines: 0}, + }, + filenames: []string{ + "unsafe-jdk-with-imports.java", + }, + expected: []finding.Finding{ + { + Probe: Probe, + Message: "Java code imports the Unsafe class", + Outcome: finding.OutcomeTrue, + Remediation: &finding.Remediation{ + Text: "Visit the OpenSSF Memory Safety SIG guidance on how to make your project memory safe.\nGuidance for [Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-memory-safe-by-default-languages.md)\nGuidance for [Non Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-non-memory-safe-by-default-languages.md)", + Effort: 2, + }, + Location: &finding.Location{Path: "unsafe-jdk-with-imports.java", LineStart: toUintPointer(3)}, + }, + }, + err: nil, + }, + { + name: "Java - unsafe in jdk.internal.misc without imports", + repoLanguages: []clients.Language{ + {Name: clients.Java, NumLines: 0}, + }, + filenames: []string{ + "unsafe-jdk-without-imports.java", + }, + expected: []finding.Finding{ + { + Probe: Probe, + Message: "Java code uses the Unsafe class", + Outcome: finding.OutcomeTrue, + Remediation: &finding.Remediation{ + Text: "Visit the OpenSSF Memory Safety SIG guidance on how to make your project memory safe.\nGuidance for [Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-memory-safe-by-default-languages.md)\nGuidance for [Non Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-non-memory-safe-by-default-languages.md)", + Effort: 2, + }, + Location: &finding.Location{Path: "unsafe-jdk-without-imports.java", LineStart: toUintPointer(15)}, + }, + { + Probe: Probe, + Message: "Java code uses the Unsafe class", + Outcome: finding.OutcomeTrue, + Remediation: &finding.Remediation{ + Text: "Visit the OpenSSF Memory Safety SIG guidance on how to make your project memory safe.\nGuidance for [Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-memory-safe-by-default-languages.md)\nGuidance for [Non Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-non-memory-safe-by-default-languages.md)", + Effort: 2, + }, + Location: &finding.Location{Path: "unsafe-jdk-without-imports.java", LineStart: toUintPointer(16)}, + }, + { + Probe: Probe, + Message: "Java code uses the Unsafe class", + Outcome: finding.OutcomeTrue, + Remediation: &finding.Remediation{ + Text: "Visit the OpenSSF Memory Safety SIG guidance on how to make your project memory safe.\nGuidance for [Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-memory-safe-by-default-languages.md)\nGuidance for [Non Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-non-memory-safe-by-default-languages.md)", + Effort: 2, + }, + Location: &finding.Location{Path: "unsafe-jdk-without-imports.java", LineStart: toUintPointer(18)}, + }, + }, + err: nil, + }, + { + name: "Java - unsafe with safe with imports", + repoLanguages: []clients.Language{ + {Name: clients.Java, NumLines: 0}, + }, + filenames: []string{ + "unsafe-sun-with-imports.java", + "unsafe-jdk-with-imports.java", + "safe-no-imports.java", + }, + expected: []finding.Finding{ + { + Probe: Probe, + Message: "Java code imports the Unsafe class", + Outcome: finding.OutcomeTrue, + Remediation: &finding.Remediation{ + Text: "Visit the OpenSSF Memory Safety SIG guidance on how to make your project memory safe.\nGuidance for [Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-memory-safe-by-default-languages.md)\nGuidance for [Non Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-non-memory-safe-by-default-languages.md)", + Effort: 2, + }, + Location: &finding.Location{Path: "unsafe-sun-with-imports.java", LineStart: toUintPointer(3)}, + }, + { + Probe: Probe, + Message: "Java code imports the Unsafe class", + Outcome: finding.OutcomeTrue, + Remediation: &finding.Remediation{ + Text: "Visit the OpenSSF Memory Safety SIG guidance on how to make your project memory safe.\nGuidance for [Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-memory-safe-by-default-languages.md)\nGuidance for [Non Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-non-memory-safe-by-default-languages.md)", + Effort: 2, + }, + Location: &finding.Location{Path: "unsafe-jdk-with-imports.java", LineStart: toUintPointer(3)}, + }, + }, + err: nil, + }, + { + name: "Java - malformed file with unsafe with imports", + repoLanguages: []clients.Language{ + {Name: clients.Java, NumLines: 0}, + }, + filenames: []string{ + "unsafe-sun-with-imports.java", + "unsafe-jdk-with-imports.java", + }, + expected: []finding.Finding{ + { + Probe: Probe, + Message: "Java code imports the Unsafe class", + Outcome: finding.OutcomeTrue, + Remediation: &finding.Remediation{ + Text: "Visit the OpenSSF Memory Safety SIG guidance on how to make your project memory safe.\nGuidance for [Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-memory-safe-by-default-languages.md)\nGuidance for [Non Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-non-memory-safe-by-default-languages.md)", + Effort: 2, + }, + Location: &finding.Location{Path: "unsafe-sun-with-imports.java", LineStart: toUintPointer(3)}, + }, + { + Probe: Probe, + Message: "Java code imports the Unsafe class", + Outcome: finding.OutcomeTrue, + Remediation: &finding.Remediation{ + Text: "Visit the OpenSSF Memory Safety SIG guidance on how to make your project memory safe.\nGuidance for [Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-memory-safe-by-default-languages.md)\nGuidance for [Non Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-non-memory-safe-by-default-languages.md)", + Effort: 2, + }, + Location: &finding.Location{Path: "unsafe-jdk-with-imports.java", LineStart: toUintPointer(3)}, + }, + }, + err: nil, + }, // all languages { @@ -343,6 +588,7 @@ func Test_Run(t *testing.T) { filenames: []string{ "safe-no-imports.go", "safe-explicit.csproj", + "safe-no-imports.java", }, expected: []finding.Finding{ { @@ -354,13 +600,15 @@ func Test_Run(t *testing.T) { err: nil, }, { - name: "All Languages - go safe csharp unsafe", + name: "All Languages - go safe csharp unsafe java unsafe", repoLanguages: []clients.Language{ {Name: clients.All, NumLines: 0}, }, filenames: []string{ "safe-no-imports.go", "unsafe.csproj", + "unsafe-sun-with-imports.java", + "unsafe-jdk-with-imports.java", }, expected: []finding.Finding{ { @@ -373,17 +621,83 @@ func Test_Run(t *testing.T) { }, Location: &finding.Location{Path: "unsafe.csproj"}, }, + { + Probe: Probe, + Message: "Java code imports the Unsafe class", + Outcome: finding.OutcomeTrue, + Remediation: &finding.Remediation{ + Text: "Visit the OpenSSF Memory Safety SIG guidance on how to make your project memory safe.\nGuidance for [Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-memory-safe-by-default-languages.md)\nGuidance for [Non Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-non-memory-safe-by-default-languages.md)", + Effort: 2, + }, + Location: &finding.Location{Path: "unsafe-sun-with-imports.java", LineStart: toUintPointer(3)}, + }, + { + Probe: Probe, + Message: "Java code imports the Unsafe class", + Outcome: finding.OutcomeTrue, + Remediation: &finding.Remediation{ + Text: "Visit the OpenSSF Memory Safety SIG guidance on how to make your project memory safe.\nGuidance for [Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-memory-safe-by-default-languages.md)\nGuidance for [Non Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-non-memory-safe-by-default-languages.md)", + Effort: 2, + }, + Location: &finding.Location{Path: "unsafe-jdk-with-imports.java", LineStart: toUintPointer(3)}, + }, }, err: nil, }, { - name: "All Languages - go unsafe csharp safe", + name: "All Languages - go unsafe csharp safe java unsafe", repoLanguages: []clients.Language{ {Name: clients.All, NumLines: 0}, }, filenames: []string{ "unsafe.go", "safe-explicit.csproj", + "unsafe-sun-with-imports.java", + "unsafe-jdk-with-imports.java", + }, + expected: []finding.Finding{ + { + Probe: Probe, + Message: "Golang code uses the unsafe package", + Outcome: finding.OutcomeTrue, + Remediation: &finding.Remediation{ + Text: "Visit the OpenSSF Memory Safety SIG guidance on how to make your project memory safe.\nGuidance for [Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-memory-safe-by-default-languages.md)\nGuidance for [Non Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-non-memory-safe-by-default-languages.md)", + Effort: 2, + }, + Location: &finding.Location{Path: "unsafe.go", LineStart: toUintPointer(4)}, + }, + { + Probe: Probe, + Message: "Java code imports the Unsafe class", + Outcome: finding.OutcomeTrue, + Remediation: &finding.Remediation{ + Text: "Visit the OpenSSF Memory Safety SIG guidance on how to make your project memory safe.\nGuidance for [Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-memory-safe-by-default-languages.md)\nGuidance for [Non Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-non-memory-safe-by-default-languages.md)", + Effort: 2, + }, + Location: &finding.Location{Path: "unsafe-sun-with-imports.java", LineStart: toUintPointer(3)}, + }, + { + Probe: Probe, + Message: "Java code imports the Unsafe class", + Outcome: finding.OutcomeTrue, + Remediation: &finding.Remediation{ + Text: "Visit the OpenSSF Memory Safety SIG guidance on how to make your project memory safe.\nGuidance for [Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-memory-safe-by-default-languages.md)\nGuidance for [Non Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-non-memory-safe-by-default-languages.md)", + Effort: 2, + }, + Location: &finding.Location{Path: "unsafe-jdk-with-imports.java", LineStart: toUintPointer(3)}, + }, + }, + err: nil, + }, + { + name: "All Languages - go unsafe csharp unsafe java safe", + repoLanguages: []clients.Language{ + {Name: clients.All, NumLines: 0}, + }, + filenames: []string{ + "unsafe.go", + "unsafe.csproj", + "safe-no-imports.java", }, expected: []finding.Finding{ { @@ -396,6 +710,16 @@ func Test_Run(t *testing.T) { }, Location: &finding.Location{Path: "unsafe.go", LineStart: toUintPointer(4)}, }, + { + Probe: Probe, + Message: "C# project file allows the use of unsafe blocks", + Outcome: finding.OutcomeTrue, + Remediation: &finding.Remediation{ + Text: "Visit the OpenSSF Memory Safety SIG guidance on how to make your project memory safe.\nGuidance for [Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-memory-safe-by-default-languages.md)\nGuidance for [Non Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-non-memory-safe-by-default-languages.md)", + Effort: 2, + }, + Location: &finding.Location{Path: "unsafe.csproj"}, + }, }, err: nil, }, @@ -407,6 +731,8 @@ func Test_Run(t *testing.T) { filenames: []string{ "unsafe.go", "unsafe.csproj", + "unsafe-sun-with-imports.java", + "unsafe-jdk-with-imports.java", }, expected: []finding.Finding{ { @@ -429,6 +755,26 @@ func Test_Run(t *testing.T) { }, Location: &finding.Location{Path: "unsafe.csproj"}, }, + { + Probe: Probe, + Message: "Java code imports the Unsafe class", + Outcome: finding.OutcomeTrue, + Remediation: &finding.Remediation{ + Text: "Visit the OpenSSF Memory Safety SIG guidance on how to make your project memory safe.\nGuidance for [Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-memory-safe-by-default-languages.md)\nGuidance for [Non Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-non-memory-safe-by-default-languages.md)", + Effort: 2, + }, + Location: &finding.Location{Path: "unsafe-sun-with-imports.java", LineStart: toUintPointer(3)}, + }, + { + Probe: Probe, + Message: "Java code imports the Unsafe class", + Outcome: finding.OutcomeTrue, + Remediation: &finding.Remediation{ + Text: "Visit the OpenSSF Memory Safety SIG guidance on how to make your project memory safe.\nGuidance for [Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-memory-safe-by-default-languages.md)\nGuidance for [Non Memory-Safe By Default Languages](https://github.com/ossf/Memory-Safety/blob/main/docs/best-practice-non-memory-safe-by-default-languages.md)", + Effort: 2, + }, + Location: &finding.Location{Path: "unsafe-jdk-with-imports.java", LineStart: toUintPointer(3)}, + }, }, err: nil, }, @@ -504,6 +850,11 @@ func Test_Run_Error_OnMatchingFileContentDo(t *testing.T) { repoLanguages: []clients.Language{{Name: clients.Go, NumLines: 0}}, expectedErr: fmt.Errorf("error while running function for language Check if Go code uses the unsafe package: error during ListFiles: error"), }, + { + name: "java error", + repoLanguages: []clients.Language{{Name: clients.Java, NumLines: 0}}, + expectedErr: fmt.Errorf("error while running function for language Check if Java code uses the Unsafe class: error during ListFiles: error"), + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/probes/unsafeblock/testdata/safe-no-imports.java b/probes/unsafeblock/testdata/safe-no-imports.java new file mode 100644 index 00000000000..82831d166cd --- /dev/null +++ b/probes/unsafeblock/testdata/safe-no-imports.java @@ -0,0 +1,15 @@ +package foo; + +/* +import jdk.internal.misc.Unsafe; +*/ +// import jdk.internal.misc.Unsafe; +public class SafeFoo { + public static void main(String[] args) { + /* + jdk.internal.misc.Unsafe.class.getInstance(); + */ + // jdk.internal.misc.Unsafe.class.getInstance(); + System.out.println("import jdk.internal.misc.Unsafe;"); + } +} diff --git a/probes/unsafeblock/testdata/safe-with-imports.java b/probes/unsafeblock/testdata/safe-with-imports.java new file mode 100644 index 00000000000..e23ec28e4dd --- /dev/null +++ b/probes/unsafeblock/testdata/safe-with-imports.java @@ -0,0 +1,10 @@ +package foo; + +import java.lang.String; +// import jdk.internal.misc.Unsafe; + +public class SafeFoo { + public static void main(String[] args) { + System.out.println("Foo!"); + } +} diff --git a/probes/unsafeblock/testdata/unsafe-jdk-with-imports.java b/probes/unsafeblock/testdata/unsafe-jdk-with-imports.java new file mode 100644 index 00000000000..88a232d97cb --- /dev/null +++ b/probes/unsafeblock/testdata/unsafe-jdk-with-imports.java @@ -0,0 +1,22 @@ +package foo; + +import jdk.internal.misc.Unsafe; + +import java.lang.reflect.Field; + +public class UnsafeFoo { + public static void main(final String[] args) throws NoSuchFieldException, IllegalAccessException { + final long address = getUnsafe().allocateMemory(0); + for (final String s : args) { + for (final char c : s.toCharArray()) { + getUnsafe().putChar(address, c); + } + } + } + + private static Unsafe getUnsafe() throws IllegalAccessException, NoSuchFieldException { + final Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (Unsafe) f.get(null); + } +} diff --git a/probes/unsafeblock/testdata/unsafe-jdk-without-imports.java b/probes/unsafeblock/testdata/unsafe-jdk-without-imports.java new file mode 100644 index 00000000000..48733e00434 --- /dev/null +++ b/probes/unsafeblock/testdata/unsafe-jdk-without-imports.java @@ -0,0 +1,20 @@ +package foo; + +import java.lang.reflect.Field; + +public class UnsafeFoo { + public static void main(final String[] args) throws NoSuchFieldException, IllegalAccessException { + final long address = getUnsafe().allocateMemory(0); + for (final String s : args) { + for (final char c : s.toCharArray()) { + getUnsafe().putChar(address, c); + } + } + } + + private static jdk . internal . misc . Unsafe getUnsafe() throws IllegalAccessException, NoSuchFieldException { + final Field f = jdk . internal . misc . Unsafe . class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (jdk . internal . misc . Unsafe) f.get(null); + } +} diff --git a/probes/unsafeblock/testdata/unsafe-sun-with-imports.java b/probes/unsafeblock/testdata/unsafe-sun-with-imports.java new file mode 100644 index 00000000000..10aa4897b5a --- /dev/null +++ b/probes/unsafeblock/testdata/unsafe-sun-with-imports.java @@ -0,0 +1,22 @@ +package foo; + + import sun . misc . Unsafe ; + +import java.lang.reflect.Field; + +public class UnsafeFoo { + public static void main(final String[] args) throws NoSuchFieldException, IllegalAccessException { + final long address = getUnsafe().allocateMemory(0); + for (final String s : args) { + for (final char c : s.toCharArray()) { + getUnsafe().putChar(address, c); + } + } + } + + private static Unsafe getUnsafe() throws IllegalAccessException, NoSuchFieldException { + final Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (Unsafe) f.get(null); + } +} diff --git a/probes/unsafeblock/testdata/unsafe-sun-without-imports.java b/probes/unsafeblock/testdata/unsafe-sun-without-imports.java new file mode 100644 index 00000000000..b407fd6bf6c --- /dev/null +++ b/probes/unsafeblock/testdata/unsafe-sun-without-imports.java @@ -0,0 +1,29 @@ +package foo; + +import java.lang.reflect.Field; + +/* + * Strip this: sun.misc.Unsafe + */ +public class UnsafeFoo { + public static void main(final String[] args) throws NoSuchFieldException, IllegalAccessException { + final long address = getUnsafe().allocateMemory(0); + for (final String s : args) { + for (final char c : s.toCharArray()) { + getUnsafe().putChar(address, c); + } + } + for (final char c : "Strip this: sun.misc.Unsafe".toCharArray()) { + getUnsafe().putChar(address, c); + } + } + + private static sun.misc.Unsafe getUnsafe() throws IllegalAccessException, NoSuchFieldException { + final Field f = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (sun.misc.Unsafe) f.get(null); + } +} +/* + * Strip this: sun.misc.Unsafe + */