chore: remove orphaned MaterializingArtifactWriter, gitignore healthcheck output
DefaultMaterializingArtifactWriter and its MaterializingArtifactWriter interface
were test-only orphans after the sandbox dual-write retirement (95e79bf) - no
production wiring, never instantiated outside their own test. Delete all three.
Add healthcheck_*.txt to .gitignore (one-off generated output).
This commit is contained in:
@@ -58,6 +58,7 @@ kotlin-ide/
|
|||||||
CLAUDE.md
|
CLAUDE.md
|
||||||
scripts/
|
scripts/
|
||||||
test_output.log
|
test_output.log
|
||||||
|
healthcheck_*.txt
|
||||||
**/xcuserdata
|
**/xcuserdata
|
||||||
.test-federation.*
|
.test-federation.*
|
||||||
bin/
|
bin/
|
||||||
|
|||||||
-14
@@ -1,14 +0,0 @@
|
|||||||
package com.correx.core.artifacts
|
|
||||||
|
|
||||||
import com.correx.core.artifacts.kind.FileWrittenArtifact
|
|
||||||
import com.correx.core.artifacts.kind.FileWrittenPayload
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
interface MaterializingArtifactWriter {
|
|
||||||
suspend fun materialize(payload: FileWrittenPayload, sandboxRoot: Path): MaterializationResult
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed interface MaterializationResult {
|
|
||||||
data class Success(val artifact: FileWrittenArtifact, val resolvedPath: Path) : MaterializationResult
|
|
||||||
data class Failure(val reason: String) : MaterializationResult
|
|
||||||
}
|
|
||||||
-76
@@ -1,76 +0,0 @@
|
|||||||
package com.correx.infrastructure.artifactscas
|
|
||||||
|
|
||||||
import com.correx.core.artifacts.MaterializationResult
|
|
||||||
import com.correx.core.artifacts.MaterializingArtifactWriter
|
|
||||||
import com.correx.core.artifacts.kind.FileWrittenArtifact
|
|
||||||
import com.correx.core.artifacts.kind.FileWrittenPayload
|
|
||||||
import com.correx.infrastructure.artifactscas.segment.SegmentLayout
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import org.slf4j.Logger
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.Path
|
|
||||||
import java.nio.file.attribute.PosixFilePermissions
|
|
||||||
|
|
||||||
class DefaultMaterializingArtifactWriter : MaterializingArtifactWriter {
|
|
||||||
|
|
||||||
private companion object {
|
|
||||||
val log: Logger = LoggerFactory.getLogger(DefaultMaterializingArtifactWriter::class.java)
|
|
||||||
const val DEFAULT_MODE = "644"
|
|
||||||
const val OCTAL_RADIX = 8
|
|
||||||
const val POSIX_PERMISSION_BITS = 9
|
|
||||||
const val HEX_RADIX = 16
|
|
||||||
const val BYTE_MASK = 0xFF
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("ReturnCount")
|
|
||||||
override suspend fun materialize(payload: FileWrittenPayload, sandboxRoot: Path): MaterializationResult {
|
|
||||||
if (payload.path.isBlank()) return MaterializationResult.Failure("path is blank")
|
|
||||||
val payloadPath = Path.of(payload.path)
|
|
||||||
if (payloadPath.isAbsolute) return MaterializationResult.Failure("path must be relative: ${payload.path}")
|
|
||||||
if (payloadPath.any { it.toString() == ".." }) {
|
|
||||||
return MaterializationResult.Failure("path must not contain '..': ${payload.path}")
|
|
||||||
}
|
|
||||||
|
|
||||||
val resolvedPath = sandboxRoot.resolve(payloadPath).normalize()
|
|
||||||
if (!resolvedPath.startsWith(sandboxRoot)) {
|
|
||||||
return MaterializationResult.Failure("path escapes sandbox: ${payload.path}")
|
|
||||||
}
|
|
||||||
|
|
||||||
return runCatching {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
resolvedPath.parent?.let { Files.createDirectories(it) }
|
|
||||||
Files.writeString(resolvedPath, payload.content)
|
|
||||||
runCatching {
|
|
||||||
val octal = payload.mode.trimStart('0').ifEmpty { DEFAULT_MODE }.toInt(OCTAL_RADIX)
|
|
||||||
val perms = PosixFilePermissions.fromString(octalToSymbolic(octal))
|
|
||||||
Files.setPosixFilePermissions(resolvedPath, perms)
|
|
||||||
}.onFailure {
|
|
||||||
log.warn("Failed to set permissions on {}: {}", resolvedPath, it.message)
|
|
||||||
}
|
|
||||||
val bytes = payload.content.toByteArray(Charsets.UTF_8)
|
|
||||||
val hash = SegmentLayout.blake3(bytes).toHex()
|
|
||||||
MaterializationResult.Success(
|
|
||||||
artifact = FileWrittenArtifact(
|
|
||||||
path = payload.path,
|
|
||||||
contentHash = hash,
|
|
||||||
size = bytes.size.toLong(),
|
|
||||||
mode = payload.mode,
|
|
||||||
),
|
|
||||||
resolvedPath = resolvedPath,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}.getOrElse { MaterializationResult.Failure("IO error: ${it.message}") }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun octalToSymbolic(octal: Int): String {
|
|
||||||
val perms = "rwxrwxrwx"
|
|
||||||
return CharArray(POSIX_PERMISSION_BITS) { i ->
|
|
||||||
if ((octal shr (POSIX_PERMISSION_BITS - 1 - i)) and 1 == 1) perms[i] else '-'
|
|
||||||
}.let { String(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun ByteArray.toHex(): String =
|
|
||||||
joinToString("") { it.toInt().and(BYTE_MASK).toString(HEX_RADIX).padStart(2, '0') }
|
|
||||||
}
|
|
||||||
-72
@@ -1,72 +0,0 @@
|
|||||||
package com.correx.infrastructure.artifactscas
|
|
||||||
|
|
||||||
import com.correx.core.artifacts.MaterializationResult
|
|
||||||
import com.correx.core.artifacts.kind.FileWrittenPayload
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.junit.jupiter.api.Assertions.assertFalse
|
|
||||||
import org.junit.jupiter.api.Assertions.assertInstanceOf
|
|
||||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
|
||||||
import org.junit.jupiter.api.Assertions.assertTrue
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.junit.jupiter.api.io.TempDir
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
class DefaultMaterializingArtifactWriterTest {
|
|
||||||
|
|
||||||
private val writer = DefaultMaterializingArtifactWriter()
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `valid payload materializes successfully with correct path, non-empty hash and correct size`(
|
|
||||||
@TempDir sandbox: Path,
|
|
||||||
): Unit = runBlocking {
|
|
||||||
val content = "hello world"
|
|
||||||
val payload = FileWrittenPayload(path = "subdir/hello.txt", content = content, mode = "0644")
|
|
||||||
|
|
||||||
val result = writer.materialize(payload, sandbox)
|
|
||||||
|
|
||||||
assertInstanceOf(MaterializationResult.Success::class.java, result)
|
|
||||||
result as MaterializationResult.Success
|
|
||||||
assertFalse(result.artifact.contentHash.isBlank(), "contentHash must not be blank")
|
|
||||||
assertTrue(result.artifact.size > 0, "size must be positive")
|
|
||||||
assertTrue(result.resolvedPath.startsWith(sandbox), "resolved path must be inside sandbox")
|
|
||||||
assertNotNull(result.resolvedPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `absolute path returns failure`(@TempDir sandbox: Path): Unit = runBlocking {
|
|
||||||
val payload = FileWrittenPayload(path = "/etc/passwd", content = "x")
|
|
||||||
|
|
||||||
val result = writer.materialize(payload, sandbox)
|
|
||||||
|
|
||||||
assertInstanceOf(MaterializationResult.Failure::class.java, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `path with dot-dot returns failure`(@TempDir sandbox: Path): Unit = runBlocking {
|
|
||||||
val payload = FileWrittenPayload(path = "../escape.txt", content = "x")
|
|
||||||
|
|
||||||
val result = writer.materialize(payload, sandbox)
|
|
||||||
|
|
||||||
assertInstanceOf(MaterializationResult.Failure::class.java, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `blank path returns failure`(@TempDir sandbox: Path): Unit = runBlocking {
|
|
||||||
val payload = FileWrittenPayload(path = " ", content = "x")
|
|
||||||
|
|
||||||
val result = writer.materialize(payload, sandbox)
|
|
||||||
|
|
||||||
assertInstanceOf(MaterializationResult.Failure::class.java, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `path that escapes sandbox after normalization returns failure`(@TempDir sandbox: Path): Unit = runBlocking {
|
|
||||||
// Construct a path like "subdir/../../../../../../tmp/evil" that normalizes outside sandbox
|
|
||||||
val escaping = "subdir/" + "../".repeat(20) + "evil.txt"
|
|
||||||
val payload = FileWrittenPayload(path = escaping, content = "x")
|
|
||||||
|
|
||||||
val result = writer.materialize(payload, sandbox)
|
|
||||||
|
|
||||||
assertInstanceOf(MaterializationResult.Failure::class.java, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user