Skip to content

Commit

Permalink
feat: Scan mod- and server pack using Nekodetector and jNeedle
Browse files Browse the repository at this point in the history
  • Loading branch information
Griefed committed Aug 10, 2024
1 parent 24fda66 commit f18e134
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 8 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ allprojects {
gradlePluginPortal()
google()
mavenCentral()
maven("https://jitpack.io")
}

tasks.withType<Test> {
Expand Down
6 changes: 5 additions & 1 deletion serverpackcreator-api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

import java.net.URI
import java.util.prefs.Preferences

plugins {
Expand All @@ -9,6 +10,7 @@ plugins {

repositories {
mavenCentral()
maven("https://jitpack.io")
}

dependencies {
Expand All @@ -29,8 +31,10 @@ dependencies {
api("org.pf4j:pf4j:3.11.0")
api("org.bouncycastle:bcpkix-jdk18on:1.78")

api("com.github.MCRcortex:nekodetector:Version-1.1-pre")
api("dev.kosmx.needle:jneedle:1.0.1")

testImplementation("org.jetbrains.kotlin:kotlin-test:1.9.24")
testImplementation("org.jetbrains.kotlin:kotlin-test:1.9.23")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.3")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.10.2")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import de.griefed.serverpackcreator.api.ApiPlugins
import de.griefed.serverpackcreator.api.ApiProperties
import de.griefed.serverpackcreator.api.utilities.SPCConfigCheckListener
import de.griefed.serverpackcreator.api.utilities.SPCGenericListener
import de.griefed.serverpackcreator.api.utilities.SecurityScans
import de.griefed.serverpackcreator.api.utilities.common.*
import de.griefed.serverpackcreator.api.versionmeta.VersionMeta
import net.lingala.zip4j.ZipFile
Expand Down Expand Up @@ -166,6 +167,13 @@ class ConfigurationHandler(
packConfig.setModsWhitelist(apiProperties.whitelistedMods().toMutableList())
}

val modpack = File(packConfig.modpackDir)
log.info("Performing security scans")
log.info("Performing Nekodetector scan")
configCheck.otherErrors.addAll(SecurityScans.scanUsingNekodetector(modpack.toPath()))
log.info("Performing jNeedle scan")
configCheck.otherErrors.addAll(SecurityScans.scanUsingJNeedle(modpack.toPath()))

if (!checkIconAndProperties(packConfig.serverIconPath)) {
configCheck.serverIconErrors.add(Translations.configuration_log_error_servericon(packConfig.serverIconPath))
log.error("The specified server-icon does not exist: ${packConfig.serverIconPath}")
Expand All @@ -190,7 +198,6 @@ class ConfigurationHandler(
log.error("No read-permission for ${packConfig.serverPropertiesPath}")
}

val modpack = File(packConfig.modpackDir)
if (modpack.isDirectory) {
isDir(packConfig, configCheck)
} else if (modpack.isFile && modpack.name.endsWith("zip")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ import java.util.*
*/
class ServerPackGeneration(
/**
* Whether the generation was successful.
* The generated server pack as a file. This usually points towards a directory on the file-system.
*/
val success: Boolean,
val serverPack: File,

/**
* The generated server pack as a file. This usually points towards a directory on the file-system.
* List of errors encountered during server pack generation.
*/
val serverPack: File,
val errors: List<String>,

/**
* The server pack ZIP-archive, if one was generated. [Optional] for ease of use, as not every server pack generation
Expand All @@ -56,4 +56,12 @@ class ServerPackGeneration(
* through this list and remove the path to the server pack from each entry to receive a list of relative paths.
*/
val files: List<File>
)
) {
/**
* Whether the generation was successful.
*/
val success: Boolean
get() {
return errors.isEmpty()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -386,9 +386,16 @@ class ServerPackHandler(
runGenericEventListeners()
log.debug("Generation took ${generationStopWatch.stop().getTime()}")

log.info("Performing security scans")
val findings = mutableListOf<String>()
log.info("Performing Nekodetector scan")
findings.addAll(SecurityScans.scanUsingNekodetector(serverPack.toPath()))
log.info("Performing jNeedle scan")
findings.addAll(SecurityScans.scanUsingJNeedle(serverPack.toPath()))

return ServerPackGeneration(
serverPack.isDirectory && !serverPack.listFiles().isNullOrEmpty(),
serverPack,
findings,
serverPackZip,
packConfig,
files
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/* Copyright (C) 2024 Griefed
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*
* The full license can be found at https:github.com/Griefed/ServerPackCreator/blob/main/LICENSE
*/
package de.griefed.serverpackcreator.api.utilities

import me.cortex.jarscanner.Main
import org.apache.logging.log4j.kotlin.cachedLoggerOf
import java.nio.file.Path
import dev.kosmx.needle.CheckWrapper

/**
* Various methods to perform security-related scans, such as Nekodetector.
*
* @author Griefed
*/
class SecurityScans {

companion object {
val log by lazy { cachedLoggerOf(SecurityScans::class.java) }

init {
CheckWrapper.init()
}

/**
* Uses MCRcortex's nekodetector to detect files infected by the fractureiser malware.
* The code can be found at [MCRcortex/nekodetector](https://github.com/MCRcortex/nekodetector)
*
* Initially provided via a plugin, available at [Griefed/spc-nekodetector-plugin](https://github.com/Griefed/spc-nekodetector-plugin)
* @author Griefed
*/
fun scanUsingNekodetector(destination: Path) : List<String> {
val results = mutableListOf<String>()
try {
log.info("Scanning $destination for infections using Nekodetector...")
val output: (String) -> String = { "" }
val run = Main.run(1, destination, true, output)
if (run.stage1Detections.isNotEmpty()) {
results.add("Stage 1 infections:")
for (entry in run.stage1Detections) {
results.add(entry)
}
}
if (run.stage2Detections.isNotEmpty()) {
results.add("Stage 2 infections:")
for (entry in run.stage2Detections) {
results.add(entry)
}
}
} catch (ex: Exception) {
log.error("Error during Nekodetector scan.", ex)
}
return results
}

/**
* Uses KosmX's jNeedle (or Needle) to detect files infected by the malware.
* The code can be found at [KosmX/jneedle](https://github.com/KosmX/jneedle)
*
* Initially provided via a plugin, available at [Griefed/spc-jneedle-plugin](https://github.com/Griefed/spc-jneedle-plugin)
* @author Griefed
*/
fun scanUsingJNeedle(destination: Path) : List<String> {
val results = mutableListOf<String>()
try {
log.info("Scanning $destination for infections using jNeedle...")
val run = CheckWrapper.checkPathBlocking(destination)
for (result in run) {
for (jarCheckResult in result.second) {
results.add("${jarCheckResult.status}: ${jarCheckResult.getMessage()}\n".padStart(9,' '))
}
}
} catch (ex: Exception) {
log.error("Error during jNeedle scan.", ex)
}
return results
}

}
}

0 comments on commit f18e134

Please sign in to comment.