dab6c11ea8
kage launched Chrome with --no-sandbox unconditionally, which turns off the browser's main security boundary for every run, including ordinary desktop use where the sandbox works fine. Since kage renders pages from the open web, a renderer exploit could then reach the host. Reported in #10. Keep the sandbox on by default and drop it only where it genuinely cannot initialize: inside a container, or when running as root (Chrome refuses to start a sandbox as root). Containers are detected from IN_DOCKER or the /.dockerenv marker, and there kage also sets --disable-dev-shm-usage because the default 64 MB /dev/shm is too small for the renderer on large pages. Whenever the sandbox is dropped kage says so on stderr, so it is never silent. Thanks to Dimitrios Prasakis for the report and to the commenter on Hacker News who suggested the IN_DOCKER opt-in.
76 lines
1.8 KiB
Go
76 lines
1.8 KiB
Go
package clone
|
|
|
|
import (
|
|
"sync"
|
|
"sync/atomic"
|
|
)
|
|
|
|
// maxRecordedFailures caps how many individual failures Run keeps for the final
|
|
// report, so a huge broken site cannot grow the slice without bound. The error
|
|
// counters still count every failure.
|
|
const maxRecordedFailures = 100
|
|
|
|
// stats are the live counters of a run, read by the CLI's progress ticker.
|
|
type stats struct {
|
|
pages atomic.Int64
|
|
assets atomic.Int64
|
|
pageErrors atomic.Int64
|
|
assetErrors atomic.Int64
|
|
skipped atomic.Int64 // robots-disallowed or out of budget
|
|
|
|
muFail sync.Mutex
|
|
failures []Failure
|
|
}
|
|
|
|
// Failure is one thing that went wrong, kept for the end-of-run report so the
|
|
// errors are visible as a list rather than only as a count.
|
|
type Failure struct {
|
|
Kind string // "page" or "asset"
|
|
URL string
|
|
Referer string // the page that referenced it, when known
|
|
Reason string // e.g. "HTTP 403 Forbidden"
|
|
}
|
|
|
|
func (s *stats) recordFailure(f Failure) {
|
|
s.muFail.Lock()
|
|
if len(s.failures) < maxRecordedFailures {
|
|
s.failures = append(s.failures, f)
|
|
}
|
|
s.muFail.Unlock()
|
|
}
|
|
|
|
func (s *stats) recordedFailures() []Failure {
|
|
s.muFail.Lock()
|
|
defer s.muFail.Unlock()
|
|
out := make([]Failure, len(s.failures))
|
|
copy(out, s.failures)
|
|
return out
|
|
}
|
|
|
|
// Progress is a snapshot of a run for display.
|
|
type Progress struct {
|
|
Pages int64
|
|
Assets int64
|
|
PageErrors int64
|
|
AssetErrors int64
|
|
Skipped int64
|
|
}
|
|
|
|
func (s *stats) snapshot() Progress {
|
|
return Progress{
|
|
Pages: s.pages.Load(),
|
|
Assets: s.assets.Load(),
|
|
PageErrors: s.pageErrors.Load(),
|
|
AssetErrors: s.assetErrors.Load(),
|
|
Skipped: s.skipped.Load(),
|
|
}
|
|
}
|
|
|
|
// Result is the final outcome returned by Run.
|
|
type Result struct {
|
|
Progress
|
|
OutDir string
|
|
// Failures is a capped sample of what went wrong, for the final report.
|
|
Failures []Failure
|
|
}
|