Commit 0840e54
authored
fix(snapshot azure): reject zip entries that would extract outside the temp dir (#1175)
* fix(snapshot azure): reject zip entries that would extract outside the temp dir
snapshot azure downloads each zip-deployed Web App's package and extracts
it by joining every entry name onto a temp directory, with no check that
the result stayed inside. An entry named "../../x" wrote outside the temp
directory, as the account running the snapshot, on every platform.
The S3 snapshot already guards this shape in localPathForS3Key. That rule
now lives in utils.LocalRelativePath so both paths, and any future place
an external name becomes a local path, apply the same containment check:
a segment resolving to ".." (including ".. " and "..." as Windows reads
them) is rejected, only a leading "/" is trimmed, and the remainder must
satisfy filepath.IsLocal. The zip path fails the snapshot naming the
offending entry rather than skipping it, so a tampered package is not
silently fingerprinted without part of its content.
* fix(snapshot azure): tolerate a "./" entry, pin symlink handling, export the rejection sentinels
utils.LocalRelativePath now returns ErrPathTraversal, ErrNamesNoFile and
ErrNotLocalPath so callers tell an attack apart from a harmless entry with
errors.Is rather than by matching message text. The messages are unchanged.
A "./" directory entry names the extraction directory itself and was a
no-op before the containment rule; it is skipped again instead of failing
the snapshot. A file entry that names no file still fails.
A symlink entry has always landed as a regular file because os.OpenFile
only honours permission bits; that is now explicit with Mode().Perm() and
pinned by a test, since name containment cannot survive a real symlink
that a later entry or the fingerprinter would follow out of the directory.
The unzip wrapper now wraps with %w. The app name was already prefixed by
the caller in GetAzureAppsData.
* fix(snapshot s3): keep the rejection sentinel through the object-key error
unusableS3KeyError now takes the reason as an error and wraps it with %w,
so errors.Is(err, utils.ErrPathTraversal) holds on the S3 path as it
already did through utils.ContainedPath. Message text is unchanged.
Also corrects the comment on the zip extraction open call: os.OpenFile
never creates a symlink whatever the type bits, so the choice of OpenFile
over os.Symlink is what keeps a symlink entry a regular file. What Perm()
adds is dropping setuid, setgid and sticky, which OpenFile does honour.
* fix(snapshot azure): name the entry on every extraction error and write entries with a constant mode
The rejection message now says the app cannot be reported until it is
redeployed without the entry: snapshot azure has no exclude flag and
.kosli_ignore is only read after extraction, so a changed package is the
only remedy. "..." and ".. " are legal names off Windows but the shared
rule still rejects them, so the message matters there too.
Every other extraction failure is wrapped with the entry name, and a file
"a" alongside an entry "a/b" gets the same actionable sentence the S3 path
gives an object and a prefix, instead of a bare ENOTDIR on a temp path.
Entries are written 0o600 rather than with the archive's permission bits.
The tree is deleted once fingerprinted and the fingerprint never reads a
mode, so this is fingerprint-identical; it keeps a zero-mode entry
readable, which previously failed later inside the fingerprinter, and
still drops setuid, setgid and sticky.
The two key-caused S3 rejections are package-level sentinels, matching
the utils style.
* fix(snapshot azure): say a rejected entry fails the whole snapshot, and name either side of a name collision
One app's error cancels the run in GetAzureAppsData, so a rejected entry
means no app in the environment is reported, not only the offending one.
The help text and the runtime error now say so.
A directory entry over an earlier file was reported as a parent problem,
and a file entry over an earlier directory fell through to a bare EISDIR
carrying the temp path. Both are checked up front with Lstat so the error
names the colliding entry, says which kind was there first, and fails the
same way on every platform. The parent-is-a-file case keeps its message.
* fix(snapshot azure): fail on two entries landing on one path, and on a write error surfacing at Close
O_EXCL replaces O_TRUNC so the second of two entries resolving to the same
path fails the snapshot, naming the entry, instead of silently replacing
the first. Duplicates are legal in a zip, and containment collapses "x",
"/x" and "./x" onto one path, so this was more reachable than before. It
mirrors errPathCollision on the S3 path: a package is never fingerprinted
without part of its content.
A destination Close error is now returned when Copy succeeded, since a
write error can surface only there and the entry would otherwise be
fingerprinted truncated.
Directories are created 0o700, consistent with the constant file mode.
* fix(snapshot azure): name the case-insensitive filesystem remedy on a path collision
Two entries differing only in case collide on macOS and Windows but not on
Linux, so the same package can snapshot from one machine and fail from
another. The operator has no flag to exclude an entry, so the error now
says to run the snapshot on a case-sensitive filesystem.
The escape-test table no longer indexes the last byte of an entry name,
which panicked on an empty name; that row is now in the table.
* fix(snapshot azure): name both entries of a path collision, and give the case-insensitivity advice only when it applies
unzip now records each extracted path with the entry that produced it, so
two entries resolving to one path fail with both names: "zip entry
[dir/./x]: resolves to the same local path as entry [dir/x]". That case
fails identically on every platform and the operator can see what to fix.
Only when no recorded entry resolves to the path, yet the file exists, is
the filesystem equating two names the package keeps distinct, as macOS
and Windows do for case. That branch alone advises running the snapshot
on a case-sensitive filesystem.
The escape test asserts the bracketed entry name, so the empty-name row
checks that the message still names the entry rather than passing
vacuously.1 parent 80d835a commit 0840e54
7 files changed
Lines changed: 531 additions & 56 deletions
File tree
- cmd/kosli
- internal
- aws
- azure
- utils
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
27 | 30 | | |
28 | 31 | | |
29 | 32 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
| 31 | + | |
31 | 32 | | |
32 | 33 | | |
33 | 34 | | |
| |||
556 | 557 | | |
557 | 558 | | |
558 | 559 | | |
559 | | - | |
560 | | - | |
| 560 | + | |
| 561 | + | |
561 | 562 | | |
562 | | - | |
563 | | - | |
564 | | - | |
565 | | - | |
566 | | - | |
567 | | - | |
568 | | - | |
569 | | - | |
570 | | - | |
571 | | - | |
572 | | - | |
573 | | - | |
574 | | - | |
575 | | - | |
576 | | - | |
577 | | - | |
578 | | - | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
579 | 566 | | |
580 | | - | |
581 | 567 | | |
582 | 568 | | |
583 | 569 | | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
584 | 577 | | |
585 | 578 | | |
586 | 579 | | |
587 | | - | |
588 | | - | |
| 580 | + | |
| 581 | + | |
589 | 582 | | |
590 | 583 | | |
591 | 584 | | |
| |||
597 | 590 | | |
598 | 591 | | |
599 | 592 | | |
600 | | - | |
| 593 | + | |
601 | 594 | | |
602 | 595 | | |
603 | 596 | | |
| |||
607 | 600 | | |
608 | 601 | | |
609 | 602 | | |
610 | | - | |
| 603 | + | |
611 | 604 | | |
612 | 605 | | |
613 | 606 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| 18 | + | |
18 | 19 | | |
19 | 20 | | |
20 | 21 | | |
| |||
1320 | 1321 | | |
1321 | 1322 | | |
1322 | 1323 | | |
| 1324 | + | |
| 1325 | + | |
| 1326 | + | |
| 1327 | + | |
| 1328 | + | |
| 1329 | + | |
1323 | 1330 | | |
1324 | 1331 | | |
1325 | 1332 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
19 | 20 | | |
20 | 21 | | |
21 | 22 | | |
| |||
26 | 27 | | |
27 | 28 | | |
28 | 29 | | |
| 30 | + | |
29 | 31 | | |
30 | 32 | | |
31 | 33 | | |
| |||
274 | 276 | | |
275 | 277 | | |
276 | 278 | | |
277 | | - | |
| 279 | + | |
278 | 280 | | |
279 | 281 | | |
280 | 282 | | |
| |||
329 | 331 | | |
330 | 332 | | |
331 | 333 | | |
332 | | - | |
333 | | - | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
334 | 337 | | |
335 | | - | |
336 | | - | |
337 | | - | |
338 | | - | |
339 | | - | |
340 | | - | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
341 | 344 | | |
342 | 345 | | |
343 | | - | |
344 | | - | |
345 | | - | |
346 | | - | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
347 | 351 | | |
348 | 352 | | |
349 | | - | |
350 | | - | |
351 | | - | |
352 | | - | |
| 353 | + | |
| 354 | + | |
353 | 355 | | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
354 | 359 | | |
355 | | - | |
356 | | - | |
357 | | - | |
358 | | - | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
359 | 372 | | |
| 373 | + | |
| 374 | + | |
360 | 375 | | |
361 | | - | |
362 | | - | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
363 | 416 | | |
364 | | - | |
| 417 | + | |
| 418 | + | |
365 | 419 | | |
366 | | - | |
367 | 420 | | |
368 | 421 | | |
369 | | - | |
370 | | - | |
371 | | - | |
372 | | - | |
| 422 | + | |
| 423 | + | |
373 | 424 | | |
374 | | - | |
375 | | - | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
376 | 433 | | |
377 | 434 | | |
378 | | - | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
379 | 440 | | |
380 | 441 | | |
381 | 442 | | |
| |||
0 commit comments