Extraction Hardening
This commit is contained in:
@@ -120,6 +120,9 @@ func TestExtractReadsHAKAssets(t *testing.T) {
|
||||
"source": "src",
|
||||
"assets": "assets",
|
||||
"build": "build"
|
||||
},
|
||||
"extract": {
|
||||
"archives": ["*.hak"]
|
||||
}
|
||||
}
|
||||
`)
|
||||
@@ -175,6 +178,281 @@ func TestExtractReadsHAKAssets(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractDefaultOnlyReadsConfiguredModuleArchive(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
mustMkdir(t, filepath.Join(root, "src", "module"))
|
||||
mustMkdir(t, filepath.Join(root, "assets", "mdl"))
|
||||
mustMkdir(t, filepath.Join(root, "build"))
|
||||
|
||||
mustWriteFile(t, filepath.Join(root, "nwn-tool.yaml"), `
|
||||
module:
|
||||
name: Test Module
|
||||
resref: testmod
|
||||
paths:
|
||||
source: src
|
||||
assets: assets
|
||||
build: build
|
||||
`)
|
||||
mustWriteFile(t, filepath.Join(root, "src", "module", "module.ifo.json"), `{
|
||||
"file_type": "IFO ",
|
||||
"file_version": "V3.2",
|
||||
"root": {
|
||||
"struct_type": 0,
|
||||
"fields": [
|
||||
{
|
||||
"label": "Mod_Name",
|
||||
"type": "CExoString",
|
||||
"value": "Original Module"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
`)
|
||||
mustWriteFile(t, filepath.Join(root, "assets", "mdl", "stale_asset.mdl"), "keep")
|
||||
|
||||
p, err := project.Load(root)
|
||||
if err != nil {
|
||||
t.Fatalf("load project: %v", err)
|
||||
}
|
||||
if err := p.ValidateLayout(); err != nil {
|
||||
t.Fatalf("validate layout: %v", err)
|
||||
}
|
||||
if err := p.Scan(); err != nil {
|
||||
t.Fatalf("scan: %v", err)
|
||||
}
|
||||
if _, err := BuildModule(p); err != nil {
|
||||
t.Fatalf("build module: %v", err)
|
||||
}
|
||||
if err := os.Remove(filepath.Join(root, "src", "module", "module.ifo.json")); err != nil {
|
||||
t.Fatalf("remove source file: %v", err)
|
||||
}
|
||||
if err := p.Scan(); err != nil {
|
||||
t.Fatalf("rescan before extract: %v", err)
|
||||
}
|
||||
|
||||
hakFile, err := os.Create(filepath.Join(root, "build", "vfx.hak"))
|
||||
if err != nil {
|
||||
t.Fatalf("create hak: %v", err)
|
||||
}
|
||||
if err := erf.Write(hakFile, erf.New("HAK ", []erf.Resource{
|
||||
{Name: "test_vfx", Type: 0x07D2, Data: []byte("mdl-data")},
|
||||
})); err != nil {
|
||||
t.Fatalf("write hak: %v", err)
|
||||
}
|
||||
if err := hakFile.Close(); err != nil {
|
||||
t.Fatalf("close hak: %v", err)
|
||||
}
|
||||
|
||||
result, err := Extract(p)
|
||||
if err != nil {
|
||||
t.Fatalf("extract: %v", err)
|
||||
}
|
||||
if len(result.HAKPaths) != 0 {
|
||||
t.Fatalf("expected default extract to ignore haks, got %#v", result.HAKPaths)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(root, "src", "module", "module.ifo.json")); err != nil {
|
||||
t.Fatalf("expected module source to be restored: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(root, "assets", "mdl", "stale_asset.mdl")); err != nil {
|
||||
t.Fatalf("expected asset cleanup to be skipped for module-only extract: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(root, "assets", "mdl", "test_vfx.mdl")); !os.IsNotExist(err) {
|
||||
t.Fatalf("expected hak asset not to be extracted by default, err=%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractRejectsERFArchiveTargets(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
mustMkdir(t, filepath.Join(root, "src"))
|
||||
mustMkdir(t, filepath.Join(root, "assets"))
|
||||
mustMkdir(t, filepath.Join(root, "build"))
|
||||
|
||||
mustWriteFile(t, filepath.Join(root, "nwn-tool.yaml"), `
|
||||
module:
|
||||
name: Test Module
|
||||
resref: testmod
|
||||
paths:
|
||||
source: src
|
||||
assets: assets
|
||||
build: build
|
||||
extract:
|
||||
archives:
|
||||
- imported.erf
|
||||
`)
|
||||
|
||||
erfFile, err := os.Create(filepath.Join(root, "build", "imported.erf"))
|
||||
if err != nil {
|
||||
t.Fatalf("create erf: %v", err)
|
||||
}
|
||||
if err := erf.Write(erfFile, erf.New("ERF ", nil)); err != nil {
|
||||
t.Fatalf("write erf: %v", err)
|
||||
}
|
||||
if err := erfFile.Close(); err != nil {
|
||||
t.Fatalf("close erf: %v", err)
|
||||
}
|
||||
|
||||
p, err := project.Load(root)
|
||||
if err != nil {
|
||||
t.Fatalf("load project: %v", err)
|
||||
}
|
||||
_, err = Extract(p)
|
||||
if err == nil {
|
||||
t.Fatal("expected erf extraction to fail")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "ERF extraction is unsafe") {
|
||||
t.Fatalf("expected unsafe erf error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractRejectsEscapingArchiveOverride(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
mustMkdir(t, filepath.Join(root, "src"))
|
||||
mustMkdir(t, filepath.Join(root, "assets"))
|
||||
mustMkdir(t, filepath.Join(root, "build"))
|
||||
|
||||
mustWriteFile(t, filepath.Join(root, "nwn-tool.yaml"), `
|
||||
module:
|
||||
name: Test Module
|
||||
resref: testmod
|
||||
paths:
|
||||
source: src
|
||||
assets: assets
|
||||
build: build
|
||||
`)
|
||||
|
||||
p, err := project.Load(root)
|
||||
if err != nil {
|
||||
t.Fatalf("load project: %v", err)
|
||||
}
|
||||
_, err = Extract(p, "../outside.mod")
|
||||
if err == nil {
|
||||
t.Fatal("expected escaping archive override to fail")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "must not escape paths.build") {
|
||||
t.Fatalf("expected escaping archive error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractConsumesConfiguredHAKArchiveAfterSuccess(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
mustMkdir(t, filepath.Join(root, "src"))
|
||||
mustMkdir(t, filepath.Join(root, "assets"))
|
||||
mustMkdir(t, filepath.Join(root, "build"))
|
||||
|
||||
mustWriteFile(t, filepath.Join(root, "nwn-tool.yaml"), `
|
||||
module:
|
||||
name: Test Module
|
||||
resref: testmod
|
||||
paths:
|
||||
source: src
|
||||
assets: assets
|
||||
build: build
|
||||
extract:
|
||||
archives:
|
||||
- vfx.hak
|
||||
consume_archives: true
|
||||
`)
|
||||
|
||||
hakPath := filepath.Join(root, "build", "vfx.hak")
|
||||
hakFile, err := os.Create(hakPath)
|
||||
if err != nil {
|
||||
t.Fatalf("create hak: %v", err)
|
||||
}
|
||||
if err := erf.Write(hakFile, erf.New("HAK ", []erf.Resource{
|
||||
{Name: "test_vfx", Type: 0x07D2, Data: []byte("mdl-data")},
|
||||
})); err != nil {
|
||||
t.Fatalf("write hak: %v", err)
|
||||
}
|
||||
if err := hakFile.Close(); err != nil {
|
||||
t.Fatalf("close hak: %v", err)
|
||||
}
|
||||
|
||||
p, err := project.Load(root)
|
||||
if err != nil {
|
||||
t.Fatalf("load project: %v", err)
|
||||
}
|
||||
result, err := Extract(p)
|
||||
if err != nil {
|
||||
t.Fatalf("extract: %v", err)
|
||||
}
|
||||
if len(result.DeletedArchivePaths) != 1 || result.DeletedArchivePaths[0] != hakPath {
|
||||
t.Fatalf("expected consumed hak path, got %#v", result.DeletedArchivePaths)
|
||||
}
|
||||
if _, err := os.Stat(hakPath); !os.IsNotExist(err) {
|
||||
t.Fatalf("expected hak archive to be deleted, stat err=%v", err)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(root, "assets", "mdl", "test_vfx.mdl")); err != nil {
|
||||
t.Fatalf("expected extracted hak asset: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractConsumeArchivesFalseOverridesLegacyDeleteFlag(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
mustMkdir(t, filepath.Join(root, "src", "module"))
|
||||
mustMkdir(t, filepath.Join(root, "assets"))
|
||||
mustMkdir(t, filepath.Join(root, "build"))
|
||||
|
||||
mustWriteFile(t, filepath.Join(root, "nwn-tool.yaml"), `
|
||||
module:
|
||||
name: Test Module
|
||||
resref: testmod
|
||||
paths:
|
||||
source: src
|
||||
assets: assets
|
||||
build: build
|
||||
extract:
|
||||
consume_archives: false
|
||||
delete_module_archive_after_success: true
|
||||
`)
|
||||
mustWriteFile(t, filepath.Join(root, "src", "module", "module.ifo.json"), `{
|
||||
"file_type": "IFO ",
|
||||
"file_version": "V3.2",
|
||||
"root": {
|
||||
"struct_type": 0,
|
||||
"fields": [
|
||||
{
|
||||
"label": "Mod_Name",
|
||||
"type": "CExoString",
|
||||
"value": "Original Module"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
p, err := project.Load(root)
|
||||
if err != nil {
|
||||
t.Fatalf("load project: %v", err)
|
||||
}
|
||||
if err := p.ValidateLayout(); err != nil {
|
||||
t.Fatalf("validate layout: %v", err)
|
||||
}
|
||||
if err := p.Scan(); err != nil {
|
||||
t.Fatalf("scan: %v", err)
|
||||
}
|
||||
buildResult, err := BuildModule(p)
|
||||
if err != nil {
|
||||
t.Fatalf("build module: %v", err)
|
||||
}
|
||||
if err := os.Remove(filepath.Join(root, "src", "module", "module.ifo.json")); err != nil {
|
||||
t.Fatalf("remove source file: %v", err)
|
||||
}
|
||||
if err := p.Scan(); err != nil {
|
||||
t.Fatalf("rescan before extract: %v", err)
|
||||
}
|
||||
|
||||
result, err := Extract(p)
|
||||
if err != nil {
|
||||
t.Fatalf("extract: %v", err)
|
||||
}
|
||||
if len(result.DeletedArchivePaths) != 0 {
|
||||
t.Fatalf("expected no deleted archives, got %#v", result.DeletedArchivePaths)
|
||||
}
|
||||
if _, err := os.Stat(buildResult.ModulePath); err != nil {
|
||||
t.Fatalf("expected module archive to remain, stat err=%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractRefusesAssetsWhenAssetsPathIsUnset(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
t.Chdir(root)
|
||||
@@ -226,7 +504,7 @@ paths:
|
||||
t.Fatalf("close hak: %v", err)
|
||||
}
|
||||
|
||||
_, err = Extract(p)
|
||||
_, err = Extract(p, "vfx.hak")
|
||||
if err == nil {
|
||||
t.Fatal("expected extract to fail")
|
||||
}
|
||||
@@ -285,7 +563,7 @@ paths:
|
||||
t.Fatalf("close hak: %v", err)
|
||||
}
|
||||
|
||||
_, err = Extract(p)
|
||||
_, err = Extract(p, "vfx.hak")
|
||||
if err == nil {
|
||||
t.Fatal("expected extract to fail")
|
||||
}
|
||||
@@ -357,7 +635,8 @@ paths:
|
||||
assets: assets
|
||||
build: build
|
||||
extract:
|
||||
hak_discovery: configured_haks
|
||||
archives:
|
||||
- wanted.hak
|
||||
haks:
|
||||
- name: wanted
|
||||
priority: 1
|
||||
|
||||
Reference in New Issue
Block a user