mirror of
https://github.com/gradle/wrapper-validation-action
synced 2024-11-23 17:22:01 +00:00
Merge pull request #6 from JLLeitschuh/feat/JLL/homoglyph_detector
Add a homoglyph detector for gradle-wrapper.jar files
This commit is contained in:
commit
ffa49e0d93
11 changed files with 79 additions and 13 deletions
|
@ -34,6 +34,12 @@ verify that any and all `gradle-wrapper.jar` files in the repository match the S
|
||||||
|
|
||||||
If any are found that do not match the SHA-256 checksums of our official releases, the action will fail.
|
If any are found that do not match the SHA-256 checksums of our official releases, the action will fail.
|
||||||
|
|
||||||
|
Additionally, the action will find and SHA-256 hash all
|
||||||
|
[homoglyph](https://en.wikipedia.org/wiki/Homoglyph)
|
||||||
|
variants of files named `gradle-wrapper.jar`,
|
||||||
|
for example a file named `gradlе-wrapper.jar` (which uses a Cyrillic `е` instead of `e`).
|
||||||
|
The goal is to prevent homoglyph attacks which may be very difficult to spot in a GitHub diff.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Simply add this action to your workflow **after** having checked out your source tree and **before** running any Gradle build:
|
Simply add this action to your workflow **after** having checked out your source tree and **before** running any Gradle build:
|
||||||
|
|
0
__tests__/data/invalid/gradlе-wrapper.jar
Normal file
0
__tests__/data/invalid/gradlе-wrapper.jar
Normal file
|
@ -4,7 +4,8 @@ import * as find from '../src/find'
|
||||||
test('finds test data wrapper jars', async () => {
|
test('finds test data wrapper jars', async () => {
|
||||||
const repoRoot = path.resolve('.')
|
const repoRoot = path.resolve('.')
|
||||||
const wrapperJars = await find.findWrapperJars(repoRoot)
|
const wrapperJars = await find.findWrapperJars(repoRoot)
|
||||||
expect(wrapperJars.length).toBe(2)
|
expect(wrapperJars.length).toBe(3)
|
||||||
expect(wrapperJars).toContain('__tests__/data/valid/gradle-wrapper.jar')
|
expect(wrapperJars).toContain('__tests__/data/valid/gradle-wrapper.jar')
|
||||||
expect(wrapperJars).toContain('__tests__/data/invalid/gradle-wrapper.jar')
|
expect(wrapperJars).toContain('__tests__/data/invalid/gradle-wrapper.jar')
|
||||||
|
expect(wrapperJars).toContain('__tests__/data/invalid/gradlе-wrapper.jar') // homoglyph
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,7 +4,7 @@ import * as validate from '../src/validate'
|
||||||
const baseDir = path.resolve('.')
|
const baseDir = path.resolve('.')
|
||||||
|
|
||||||
test('succeeds if all found wrapper jars are valid', async () => {
|
test('succeeds if all found wrapper jars are valid', async () => {
|
||||||
const result = await validate.findInvalidWrapperJars(baseDir, 2, false, [
|
const result = await validate.findInvalidWrapperJars(baseDir, 3, false, [
|
||||||
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
|
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -12,13 +12,14 @@ test('succeeds if all found wrapper jars are valid', async () => {
|
||||||
|
|
||||||
expect(result.toDisplayString()).toBe(
|
expect(result.toDisplayString()).toBe(
|
||||||
'✓ Found known Gradle Wrapper JAR files:\n' +
|
'✓ Found known Gradle Wrapper JAR files:\n' +
|
||||||
' e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 __tests__/data/invalid/gradle-wrapper.jar\n' +
|
' e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 __tests__/data/invalid/gradle-wrapper.jar\n' +
|
||||||
|
' e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 __tests__/data/invalid/gradlе-wrapper.jar\n' + // homoglyph
|
||||||
' 3888c76faa032ea8394b8a54e04ce2227ab1f4be64f65d450f8509fe112d38ce __tests__/data/valid/gradle-wrapper.jar'
|
' 3888c76faa032ea8394b8a54e04ce2227ab1f4be64f65d450f8509fe112d38ce __tests__/data/valid/gradle-wrapper.jar'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('fails if invalid wrapper jars are found', async () => {
|
test('fails if invalid wrapper jars are found', async () => {
|
||||||
const result = await validate.findInvalidWrapperJars(baseDir, 2, false, [])
|
const result = await validate.findInvalidWrapperJars(baseDir, 3, false, [])
|
||||||
|
|
||||||
expect(result.isValid()).toBe(false)
|
expect(result.isValid()).toBe(false)
|
||||||
|
|
||||||
|
@ -33,31 +34,37 @@ test('fails if invalid wrapper jars are found', async () => {
|
||||||
new validate.WrapperJar(
|
new validate.WrapperJar(
|
||||||
'__tests__/data/invalid/gradle-wrapper.jar',
|
'__tests__/data/invalid/gradle-wrapper.jar',
|
||||||
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
|
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
|
||||||
|
),
|
||||||
|
new validate.WrapperJar(
|
||||||
|
'__tests__/data/invalid/gradlе-wrapper.jar', // homoglyph
|
||||||
|
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
|
||||||
)
|
)
|
||||||
])
|
])
|
||||||
|
|
||||||
expect(result.toDisplayString()).toBe(
|
expect(result.toDisplayString()).toBe(
|
||||||
'✗ Found unknown Gradle Wrapper JAR files:\n' +
|
'✗ Found unknown Gradle Wrapper JAR files:\n' +
|
||||||
' e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 __tests__/data/invalid/gradle-wrapper.jar\n' +
|
' e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 __tests__/data/invalid/gradle-wrapper.jar\n' +
|
||||||
|
' e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 __tests__/data/invalid/gradlе-wrapper.jar\n' + // homoglyph
|
||||||
'✓ Found known Gradle Wrapper JAR files:\n' +
|
'✓ Found known Gradle Wrapper JAR files:\n' +
|
||||||
' 3888c76faa032ea8394b8a54e04ce2227ab1f4be64f65d450f8509fe112d38ce __tests__/data/valid/gradle-wrapper.jar'
|
' 3888c76faa032ea8394b8a54e04ce2227ab1f4be64f65d450f8509fe112d38ce __tests__/data/valid/gradle-wrapper.jar'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('fails if not enough wrapper jars are found', async () => {
|
test('fails if not enough wrapper jars are found', async () => {
|
||||||
const result = await validate.findInvalidWrapperJars(baseDir, 3, false, [])
|
const result = await validate.findInvalidWrapperJars(baseDir, 4, false, [])
|
||||||
|
|
||||||
expect(result.isValid()).toBe(false)
|
expect(result.isValid()).toBe(false)
|
||||||
|
|
||||||
expect(result.errors).toEqual([
|
expect(result.errors).toEqual([
|
||||||
'Expected to find at least 3 Gradle Wrapper JARs but got only 2'
|
'Expected to find at least 4 Gradle Wrapper JARs but got only 3'
|
||||||
])
|
])
|
||||||
|
|
||||||
expect(result.toDisplayString()).toBe(
|
expect(result.toDisplayString()).toBe(
|
||||||
'✗ Found unknown Gradle Wrapper JAR files:\n' +
|
'✗ Found unknown Gradle Wrapper JAR files:\n' +
|
||||||
' e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 __tests__/data/invalid/gradle-wrapper.jar\n' +
|
' e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 __tests__/data/invalid/gradle-wrapper.jar\n' +
|
||||||
|
' e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 __tests__/data/invalid/gradlе-wrapper.jar\n' + // homoglyph
|
||||||
'✗ Other validation errors:\n' +
|
'✗ Other validation errors:\n' +
|
||||||
' Expected to find at least 3 Gradle Wrapper JARs but got only 2\n' +
|
' Expected to find at least 4 Gradle Wrapper JARs but got only 3\n' +
|
||||||
'✓ Found known Gradle Wrapper JAR files:\n' +
|
'✓ Found known Gradle Wrapper JAR files:\n' +
|
||||||
' 3888c76faa032ea8394b8a54e04ce2227ab1f4be64f65d450f8509fe112d38ce __tests__/data/valid/gradle-wrapper.jar'
|
' 3888c76faa032ea8394b8a54e04ce2227ab1f4be64f65d450f8509fe112d38ce __tests__/data/valid/gradle-wrapper.jar'
|
||||||
)
|
)
|
||||||
|
|
41
dist/index.js
vendored
41
dist/index.js
vendored
File diff suppressed because one or more lines are too long
|
@ -7,5 +7,6 @@ module.exports = {
|
||||||
transform: {
|
transform: {
|
||||||
'^.+\\.ts$': 'ts-jest'
|
'^.+\\.ts$': 'ts-jest'
|
||||||
},
|
},
|
||||||
verbose: true
|
verbose: true,
|
||||||
|
setupFilesAfterEnv: ['./jest.setup.js']
|
||||||
}
|
}
|
||||||
|
|
1
jest.setup.js
Normal file
1
jest.setup.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
jest.setTimeout(10000) // in milliseconds
|
5
package-lock.json
generated
5
package-lock.json
generated
|
@ -6180,6 +6180,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
|
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
|
||||||
"integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI="
|
"integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI="
|
||||||
},
|
},
|
||||||
|
"unhomoglyph": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/unhomoglyph/-/unhomoglyph-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-PC/OAHE8aiTK0Gfmy0PxOlePazRn+BeCM1r4kFtkHgEnkJZgJoI7yD2yUEjsfSdLXKU1FSt/EcIZvNoKazYUTw=="
|
||||||
|
},
|
||||||
"union-value": {
|
"union-value": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
|
||||||
|
|
|
@ -26,7 +26,8 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.2.0",
|
"@actions/core": "^1.2.0",
|
||||||
"typed-rest-client": "^1.7.1"
|
"typed-rest-client": "^1.7.1",
|
||||||
|
"unhomoglyph": "^1.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^24.0.23",
|
"@types/jest": "^24.0.23",
|
||||||
|
|
4
src/declarations.d.ts
vendored
Normal file
4
src/declarations.d.ts
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
declare module 'unhomoglyph' {
|
||||||
|
function unhomoglyph(input: string): string
|
||||||
|
export = unhomoglyph
|
||||||
|
}
|
|
@ -1,13 +1,14 @@
|
||||||
import * as util from 'util'
|
import * as util from 'util'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
|
import unhomoglyph from 'unhomoglyph'
|
||||||
|
|
||||||
const readdir = util.promisify(fs.readdir)
|
const readdir = util.promisify(fs.readdir)
|
||||||
|
|
||||||
export async function findWrapperJars(baseDir: string): Promise<string[]> {
|
export async function findWrapperJars(baseDir: string): Promise<string[]> {
|
||||||
const files = await recursivelyListFiles(baseDir)
|
const files = await recursivelyListFiles(baseDir)
|
||||||
return files
|
return files
|
||||||
.filter(file => file.endsWith('gradle-wrapper.jar'))
|
.filter(file => unhomoglyph(file).endsWith('gradle-wrapper.jar'))
|
||||||
.map(wrapperJar => path.relative(baseDir, wrapperJar))
|
.map(wrapperJar => path.relative(baseDir, wrapperJar))
|
||||||
.sort((a, b) => a.localeCompare(b))
|
.sort((a, b) => a.localeCompare(b))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue