Skip to content

Commit b8a295a

Browse files
committed
fix: Rebase issues
1 parent 51c5f14 commit b8a295a

File tree

6 files changed

+213
-17
lines changed

6 files changed

+213
-17
lines changed

src/ct/launch/launch.nim

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import
22
std/[strutils, strformat, osproc],
33
../../common/[ paths, types, intel_fix, install_utils, trace_index, start_utils ],
44
../utilities/[ git, env ],
5+
../online_sharing/trace_manager,
56
../cli/[ logging, list, help ],
67
../trace/[ replay, record, run, metadata ],
78
../codetracerconf,
@@ -101,15 +102,15 @@ proc runInitial*(conf: CodetracerConf) =
101102
notSupportedCommand($conf.cmd)
102103
of StartupCommand.upload:
103104
# similar to replay/console
104-
notSupportedCommand($conf.cmd)
105-
# eventually enable?
106-
# uploadCommand(
107-
# conf.uploadLastTraceMatchingPattern,
108-
# conf.uploadTraceId,
109-
# conf.uploadTraceFolder,
110-
# replayInteractive)
105+
uploadCommand(
106+
conf.uploadLastTraceMatchingPattern,
107+
conf.uploadTraceId,
108+
conf.uploadTraceFolder,
109+
replayInteractive)
111110
of StartupCommand.download:
112-
notSupportedCommand($conf.cmd)
111+
downloadCommand(conf.traceRegistryId)
112+
of StartupCommand.cmdDelete:
113+
deleteTraceCommand(conf.traceId, conf.controlId)
113114
# eventually enable?
114115
# downloadCommand(conf.traceRegistryId)
115116
# of StartupCommand.build:
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import nimcrypto, zip/zipfiles, std/[ sequtils, strutils, strformat, os, osproc ]
2+
import ../../common/[ config, trace_index, lang ]
3+
4+
proc generateSecurePassword*(): string =
5+
var key: array[32, byte]
6+
discard randomBytes(key)
7+
8+
result = key.mapIt(it.toHex(2)).join("")
9+
return result
10+
11+
proc pkcs7Pad*(data: seq[byte], blockSize: int): seq[byte] =
12+
let padLen = blockSize - (data.len mod blockSize)
13+
result = data & repeat(cast[byte](padLen), padLen)
14+
15+
proc pkcs7Unpad*(data: seq[byte]): seq[byte] =
16+
if data.len == 0:
17+
raise newException(ValueError, "Data is empty, cannot unpad")
18+
19+
let padLen = int64(data[^1]) # Convert last byte to int64 safely
20+
if padLen <= 0 or padLen > data.len:
21+
raise newException(ValueError, "Invalid padding")
22+
23+
result = data[0 ..< data.len - padLen]
24+
25+
func toBytes*(s: string): seq[byte] =
26+
## Convert a string to the corresponding byte sequence - since strings in
27+
## nim essentially are byte sequences without any particular encoding, this
28+
## simply copies the bytes without a null terminator
29+
when nimvm:
30+
var r = newSeq[byte](s.len)
31+
for i, c in s:
32+
r[i] = cast[byte](c)
33+
r
34+
else:
35+
@(s.toOpenArrayByte(0, s.high))
36+
37+
proc encryptZip(zipFile, password: string) =
38+
var iv: seq[byte] = password.toBytes()[0..15]
39+
40+
var aes: CBC[aes256]
41+
aes.init(password.toOpenArrayByte(0, len(password) - 1), iv)
42+
43+
var zipData = readFile(zipFile).toBytes()
44+
var paddedData = pkcs7Pad(zipData, 16)
45+
var encrypted = newSeq[byte](paddedData.len)
46+
47+
aes.encrypt(paddedData, encrypted.toOpenArray(0, len(encrypted) - 1))
48+
writeFile(zipFile & ".enc", encrypted)
49+
50+
proc zipFileWithEncryption*(inputFile: string, outputZip: string, password: string) =
51+
var zip: ZipArchive
52+
if not zip.open(outputZip, fmWrite):
53+
raise newException(IOError, "Failed to create zip file: " & outputZip)
54+
55+
for file in walkDirRec(inputFile):
56+
let relPath = file.relativePath(inputFile)
57+
zip.addFile(relPath, file)
58+
59+
zip.close()
60+
encryptZip(outputZip, password)
61+
removeFile(outputZip)
62+
63+
proc uploadEncyptedZip*(file: string): (string, int) =
64+
# TODO: Plug in http client instead of curl
65+
let config = loadConfig(folder=getCurrentDir(), inTest=false)
66+
let cmd = &"curl -s -X POST -F \"file=@{file}.enc\" {config.webApiRoot}/upload"
67+
let (output, exitCode) = execCmdEx(cmd)
68+
(output, exitCode)
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import std/[ options, strutils, os, osproc, strformat, json ], ../trace/replay, ../codetracerconf, zip/zipfiles, nimcrypto
2+
import ../../common/[ config, trace_index, lang ]
3+
import ../utilities/language_detection
4+
import ../trace/[ storage_and_import, record ]
5+
import security_upload
6+
7+
proc uploadCommand*(
8+
patternArg: Option[string],
9+
traceIdArg: Option[int],
10+
traceFolderArg: Option[string],
11+
interactive: bool
12+
) =
13+
discard internalReplayOrUpload(patternArg, traceIdArg, traceFolderArg, interactive, command=StartupCommand.upload)
14+
15+
16+
proc decryptZip(encryptedFile: string, password: string, outputFile: string) =
17+
var encData = readFile(encryptedFile).toBytes()
18+
if encData.len < 16:
19+
raise newException(ValueError, "Invalid encrypted data (too short)")
20+
21+
let iv = password.toBytes()[0 ..< 16]
22+
let ciphertext = encData[16 .. ^1]
23+
let key = password.toBytes()
24+
25+
var aes: CBC[aes256]
26+
aes.init(key, iv)
27+
28+
var decrypted = newSeq[byte](encData.len)
29+
aes.decrypt(encData, decrypted.toOpenArray(0, len(decrypted) - 1))
30+
31+
var depaddedData = pkcs7Unpad(decrypted)
32+
writeFile(outputFile, depaddedData)
33+
34+
proc unzipDecryptedFile(zipFile: string, outputDir: string): (string, int) =
35+
var zip: ZipArchive
36+
if not zip.open(zipFile, fmRead):
37+
raise newException(IOError, "Failed to open decrypted ZIP: " & zipFile)
38+
39+
let traceId = trace_index.newID(false)
40+
let outPath = outputDir / "trace-" & $traceId
41+
42+
createDir(outPath)
43+
zip.extractAll(outPath)
44+
45+
zip.close()
46+
return (outPath, traceId)
47+
48+
proc downloadCommand*(traceRegistryId: string) =
49+
# We expect a traceRegistryId to have <downloadId>::<passwordKey>
50+
let stringSplit = traceRegistryId.split("//")
51+
if stringSplit.len() != 3:
52+
quit(1)
53+
else:
54+
let downloadId = stringSplit[1]
55+
let password = stringSplit[2]
56+
let zipPath = "/tmp/tmp.zip"
57+
let config = loadConfig(folder=getCurrentDir(), inTest=false)
58+
let localPath = "/tmp" / "tmp.zip.enc"
59+
# TODO: Plug in an http client
60+
let cmd = &"curl -s -o {localPath} {config.webApiRoot}/download?DownloadId={downloadId}"
61+
let (output, exitCode) = execCmdEx(cmd)
62+
63+
decryptZip(localPath, password, zipPath)
64+
65+
let (traceFolder, traceId) = unzipDecryptedFile(zipPath, os.getHomeDir() / ".local" / "share" / "codetracer")
66+
let tracePath = traceFolder / "trace.json"
67+
let traceJson = parseJson(readFile(tracePath))
68+
let traceMetadataPath = traceFolder / "trace_metadata.json"
69+
70+
var pathValue = ""
71+
72+
for item in traceJson:
73+
if item.hasKey("Path"):
74+
pathValue = item["Path"].getStr("")
75+
break
76+
77+
let lang = detectLang(pathValue, LangUnknown)
78+
discard importDbTrace(traceMetadataPath, traceId, lang, DB_SELF_CONTAINED_DEFAULT, traceRegistryId)
79+
80+
removeFile(localPath)
81+
removeFile(zipPath)
82+
83+
echo traceId
84+
85+
quit(exitCode)
86+
87+
proc deleteAndResetFields(id: int, test: bool) =
88+
updateField(id, "remoteShareDownloadId", "", test)
89+
updateField(id, "remoteShareControlId", "", test)
90+
updateField(id, "remoteShareExpireTime", -1, test)
91+
92+
proc deleteTraceCommand*(id: int, controlId: string) =
93+
let config = loadConfig(folder=getCurrentDir(), inTest=false)
94+
let cmd = &"curl -s {config.webApiRoot}/delete?ControlId={controlId}"
95+
let (output, exitCode) = execCmdEx(cmd)
96+
97+
if exitCode == 0:
98+
deleteAndResetFields(id, false)
99+
100+
quit(exitCode)

src/ct/trace/record.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ proc recordSymbols(sourceDir: string, outputFolder: string, lang: Lang) =
5454
# it's still good to have an option/opt-out, so we leave that
5555
# as a flag in the internals, but not exposed to user yet
5656
# that's why for now it's hardcoded for db
57-
const DB_SELF_CONTAINED_DEFAULT = true
57+
const DB_SELF_CONTAINED_DEFAULT* = true
5858

5959
# rr patches for ruby/other vm-s: not supported now, instead
6060
# in db backend support only direct traces

src/ct/trace/replay.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import std/[options ],
66
shell,
77
run
88

9-
proc internalReplayOrUpload(
9+
proc internalReplayOrUpload*(
1010
patternArg: Option[string],
1111
traceIdArg: Option[int],
1212
traceFolderArg: Option[string],

src/ct/trace/storage_and_import.nim

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import
22
std/[os, json, strutils, strformat, sets, algorithm],
33
../../common/[trace_index, lang, types, paths],
44
../utilities/git,
5+
../online_sharing/security_upload,
56
json_serialization
67

78
proc storeTraceFiles(paths: seq[string], traceFolder: string, lang: Lang) =
@@ -78,10 +79,12 @@ proc processSourceFoldersList*(folderSet: HashSet[string], programDir: string =
7879

7980

8081
proc importDbTrace*(
81-
traceMetadataPath: string,
82-
traceIdArg: int,
83-
lang: Lang = LangNoir,
84-
selfContained: bool = true): Trace =
82+
traceMetadataPath: string,
83+
traceIdArg: int,
84+
lang: Lang = LangNoir,
85+
selfContained: bool = true,
86+
downloadKey: string = ""
87+
): Trace =
8588
let rawTraceMetadata = readFile(traceMetadataPath)
8689
let untypedJson = parseJson(rawTraceMetadata)
8790
let program = untypedJson{"program"}.getStr()
@@ -155,8 +158,32 @@ proc importDbTrace*(
155158
calltrace = true,
156159
# for now always use FullRecord for db-backend
157160
# and ignore possible env var override
158-
calltraceMode = CalltraceMode.FullRecord)
161+
calltraceMode = CalltraceMode.FullRecord,
162+
downloadKey = downloadKey)
159163

160164
proc uploadTrace*(trace: Trace) =
161-
echo "error: uploading traces not supported currently!"
162-
quit(1)
165+
let outputZip = trace.outputFolder / "tmp.zip"
166+
let aesKey = generateSecurePassword()
167+
168+
zipFileWithEncryption(trace.outputFolder, outputZip, aesKey)
169+
170+
let (output, exitCode) = uploadEncyptedZip(outputZip)
171+
let jsonMessage = parseJson(output)
172+
let downloadKey = trace.program & "//" & jsonMessage["DownloadId"].getStr("") & "//" & aesKey
173+
174+
if jsonMessage["DownloadId"].getStr("") notin @["", "Errored"]:
175+
176+
updateField(trace.id, "remoteShareDownloadId", downloadKey, false)
177+
updateField(trace.id, "remoteShareControlId", jsonMessage["ControlId"].getStr(""), false)
178+
updateField(trace.id, "remoteShareExpireTime", jsonMessage["Expires"].getInt(), false)
179+
180+
echo downloadKey
181+
echo jsonMessage["ControlId"].getStr("")
182+
echo jsonMessage["Expires"].getInt()
183+
184+
else:
185+
echo downloadKey
186+
187+
removeFile(outputZip & ".enc")
188+
189+
quit(exitCode)

0 commit comments

Comments
 (0)