Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1436,8 +1436,8 @@ jobs:
# searching.
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
# Four targets at five minutes each, plus the build and the baseline pass.
timeout-minutes: 45
# Seven targets at five minutes each, plus the build and the baseline pass.
timeout-minutes: 55
env:
CGO_ENABLED: "0"
steps:
Expand All @@ -1461,7 +1461,7 @@ jobs:
# next search starts from nothing.
run: |
set -euo pipefail
for target in FuzzParseSize FuzzParseRecipe FuzzNameTemplate FuzzCanonicalRecipe FuzzPresetExpansion FuzzChecksumFile; do
for target in FuzzParseSize FuzzParseRecipe FuzzNameTemplate FuzzCanonicalRecipe FuzzPresetExpansion FuzzChecksumFile FuzzArchiveScan; do
echo "=== $target ==="
go test -tags "$(cat .github/build-tags)" ./internal/guard/ -run "^$" -fuzz "^${target}$" -fuzztime 5m
done
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ because it turns other people's test suites red.

### Added

- **Is this archive safe to unpack?** `tfg tool archive-scan <archive>`, and
the same on the Tools tab, reads a ZIP, TAR, TAR.GZ or gzip file and
everything in it without writing anything, and says what unpacking it
would do: entries written outside the folder, links, devices, names that
land on one file on Windows or macOS, names a system cannot write,
archives built to explode, contents that are not what the archive says.
`--unpacked_limit` reports an archive that unpacks to more than you allow.
Safe ends with code 0 and anything found with 7, the whole report on the
error channel. 7z and RAR are recognised and not read yet.
- **Documents: the `rtf` format.** A Rich Text Format document at the exact
size you ask for, read by LibreOffice, pandoc, the RichEdit control of
Windows, striprtf and Apache Tika. It holds headings, paragraphs with bold
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ tfg tool show <id> [--json] what one tool works on and takes
tfg tool checksum <file> [--algorithm sha256] [--expected <checksum>] [--json]
tfg tool checksum-write <folder> [--algorithm sha256] [--json]
tfg tool checksum-check <checksum_file> [--json]
tfg tool archive-scan <archive> [--unpacked_limit <size>] [--json]
```

Small things to do with files you already have, beside the generator. Every tool
Expand All @@ -420,6 +421,13 @@ there. `checksum-check` checks every file a checksum file lists, in the folder
the checksum file is in. Files it does not list are not looked at, the same as
`sha256sum -c`, and a path that leaves that folder is refused rather than read.

`archive-scan` looks into a ZIP, TAR, TAR.GZ or gzip file without unpacking it
and says whether unpacking it is safe: no entry written outside the folder, no
link, no device, no two names landing on one file, no name a system cannot
write, nothing built to explode and nothing damaged. `--unpacked_limit 500mb`
also reports an archive that unpacks to more than that. Safe ends with code 0,
anything found with 7.

### `tfg damage`

```
Expand Down
11 changes: 7 additions & 4 deletions internal/cli/toolcmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,11 +297,11 @@ func parseAround(fs *flag.FlagSet, args []string) ([]string, error) {

// renderToolResult prints what a tool found.
//
// A mismatch is a failure with its own code, and a failed run writes nothing
// to standard output - the same as verify - so the whole answer goes to the
// error channel, JSON included.
// A mismatch, or a conclusion the tool declares failing, is a failure with its
// own code, and a failed run writes nothing to standard output - the same as
// verify - so the whole answer goes to the error channel, JSON included.
func renderToolResult(d tool.Descriptor, r tool.Result, asJSON bool, out, errOut io.Writer) int {
failed := r.Verdict.Outcome == tool.Mismatch
failed := d.Failed(r)
w := out
if failed {
w = errOut
Expand All @@ -318,6 +318,9 @@ func renderToolResult(d tool.Descriptor, r tool.Result, asJSON bool, out, errOut
if said := r.Verdict.Said(); said != "" {
fmt.Fprintf(w, "\n%s\n", said)
}
if c, ok := d.Conclusion(r.Concluded); ok {
fmt.Fprintf(w, "\n%s\n", c.Says)
}
}
if failed {
return ExitVerify
Expand Down
12 changes: 12 additions & 0 deletions internal/engine/filename.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,15 @@ func describeForbidden(r rune) core.Said {
}
return core.Says("engine.Character", "the character %q", core.A("Character", string(r)))
}

// Unwritable says whether the generator would refuse to write a file under
// this name - for a tool asking about names somebody else made, each part of a
// path inside an archive. The same rules rather than a copy of them, so a name
// this program writes and a name it warns about are judged by one list.
//
// A part, not a path: a separator is refused here, so a caller splits first,
// and "." and ".." are refused as names of directories, which a caller asking
// about where an entry lands answers before it gets here.
func Unwritable(name string) bool {
return checkFileName("", core.Said{}, name) != nil
}
Loading
Loading