Tests/KeycaskCLITests/InitTests.swift
105 lines · 4169 bytes
1import Foundation
2import Testing
3
4@Suite struct InitTests {
5 @Test func initCreatesVaultAndPrintsPath() throws {
6 let cli = try CLI()
7 let r = try cli.run(["init"])
8 #expect(r.status == 0)
9 #expect(r.stdout.contains(cli.vault.path))
10 #expect(FileManager.default.fileExists(atPath: cli.vault.path))
11 let text = try String(contentsOf: cli.vault, encoding: .utf8)
12 #expect(text.contains("\"format\" : 1"))
13 #expect(text.contains("pbkdf2-hmac-sha256"))
14 #expect(!text.contains("entries"))
15 }
16
17 @Test func initRefusesExistingVault() throws {
18 let cli = try CLI.initialized()
19 let r = try cli.run(["init"])
20 #expect(r.status == 1)
21 #expect(r.stderr.contains("already exists"))
22 }
23
24 @Test func initWithoutPassphraseOrTTYIsUsageError() throws {
25 let cli = try CLI()
26 let r = try cli.run(["init"], passphrase: nil)
27 #expect(r.status == 2)
28 #expect(r.stderr.contains("KEYCASK_PASSPHRASE"))
29 }
30
31 @Test func emptyPassphraseIsRejected() throws {
32 let cli = try CLI()
33 let r = try cli.run(["init"], passphrase: "")
34 #expect(r.status == 1)
35 #expect(r.stderr.contains("empty"))
36 }
37
38 @Test func vaultFlagBeatsEnvironment() throws {
39 let cli = try CLI()
40 let other = cli.dir.appendingPathComponent("elsewhere.kc")
41 let r = try cli.run(["--vault", other.path, "init"])
42 #expect(r.status == 0)
43 #expect(FileManager.default.fileExists(atPath: other.path))
44 #expect(!FileManager.default.fileExists(atPath: cli.vault.path))
45 }
46
47 @Test func unknownSubcommandIsUsageError() throws {
48 let cli = try CLI()
49 let r = try cli.run(["frobnicate"])
50 #expect(r.status == 2)
51 #expect(r.stderr.contains("Usage"))
52 }
53
54 @Test func helpExitsZero() throws {
55 let cli = try CLI()
56 let r = try cli.run(["--help"])
57 #expect(r.status == 0)
58 #expect(r.stdout.contains("init"))
59 }
60
61 #if !os(Windows)
62 @Test func vaultIsPrivateOnUnix() throws {
63 let cli = try CLI.initialized()
64 let attrs = try FileManager.default.attributesOfItem(atPath: cli.vault.path)
65 let mode = (attrs[.posixPermissions] as! NSNumber).intValue & 0o777
66 #expect(mode == 0o600)
67 }
68
69 @Test func preExistingTempFileDoesNotWeakenPermissions() throws {
70 let cli = try CLI()
71 let temp = cli.dir.appendingPathComponent("vault.kc.tmp")
72 FileManager.default.createFile(
73 atPath: temp.path, contents: Data(), attributes: [.posixPermissions: 0o644])
74
75 let r = try cli.run(["init"])
76 #expect(r.status == 0)
77 let attrs = try FileManager.default.attributesOfItem(atPath: cli.vault.path)
78 let mode = (attrs[.posixPermissions] as! NSNumber).intValue & 0o777
79 #expect(mode == 0o600)
80 #expect(!FileManager.default.fileExists(atPath: temp.path))
81 }
82
83 @Test func symlinkedTempFileIsNotFollowed() throws {
84 let cli = try CLI()
85 let victim = cli.dir.appendingPathComponent("victim.txt")
86 FileManager.default.createFile(atPath: victim.path, contents: Data())
87 let temp = cli.dir.appendingPathComponent("vault.kc.tmp")
88 try FileManager.default.createSymbolicLink(at: temp, withDestinationURL: victim)
89
90 let r = try cli.run(["init"])
91 #expect(r.status == 0)
92 let victimAttrs = try FileManager.default.attributesOfItem(atPath: victim.path)
93 #expect((victimAttrs[.size] as! NSNumber).intValue == 0)
94 let attrs = try FileManager.default.attributesOfItem(atPath: cli.vault.path)
95 #expect(attrs[.type] as? FileAttributeType == .typeRegular)
96 #expect((attrs[.posixPermissions] as! NSNumber).intValue & 0o777 == 0o600)
97 }
98 #endif
99
100 @Test func noTempFileLeftBehind() throws {
101 let cli = try CLI.initialized()
102 let names = try FileManager.default.contentsOfDirectory(atPath: cli.dir.path)
103 #expect(names == ["vault.kc"])
104 }
105}