All 3 cloud models converged on a real access-control bug; fixed it + the other genuine findings (the false-positives were dropped): Security (HIGH — all 3 models): - create_file_url skipped ValidateScope: a same-skill caller could mint a PUBLIC url for a file scoped to another user/run. Now runs ValidateScope (admin-aware), skipped only for the descendant-grant case — mirroring the read tools. Other real fixes: - ValidateScope hard-coded `false` at every call site (admin branch dead) -> pass inv.CallerIsAdmin (the executor sets it via the host AdminPolicy; still false/fail-closed when no admin). Stale "no admin flag" comment corrected. - create_file_url: ExpiresInSeconds clamped BEFORE the *time.Second multiply (huge values overflowed to a negative duration that slipped under the cap, minting already-expired tokens); swallowed json.Marshal error now returned. - RegisterMeta: build the default budget WITH the configured MaxPerRun (was NewInMemorySearchBudget(nil) -> hardcoded 10, ignoring MetaDeps.MaxPerRun). - classify: all-zero scores no longer return a false-positive top-1 winner; coerceClassifyScore uses strconv.ParseFloat (rejects trailing garbage like "50extra" that fmt.Sscanf silently accepted). - file_delete: honor the descendant grant (parent can clean up a worker's artifacts) — was the lone cross-skill-reject-outright file tool. - meta tools: input caps truncate at a UTF-8 rune boundary (truncateUTF8), not mid-rune. - think: removed the dead `var _ = fmt.Errorf` import-keeper; file_save default aligned to 16 MiB (matched RegisterStore). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -124,14 +124,37 @@ func NewCreateFileURL(minter FileTokenMinter, fileStorage FileStorage, baseURL s
|
||||
}
|
||||
return "", fmt.Errorf("create_file_url: %w", err)
|
||||
}
|
||||
if meta.SkillID != inv.SkillID && !descendantFileGrant(ctx, fileStorage, inv, meta.SkillID) {
|
||||
return "", fmt.Errorf("create_file_url: file_id %q does not belong to this skill (cross-skill refs rejected)", args.FileID)
|
||||
grantedViaDescendant := false
|
||||
if meta.SkillID != inv.SkillID {
|
||||
if !descendantFileGrant(ctx, fileStorage, inv, meta.SkillID) {
|
||||
return "", fmt.Errorf("create_file_url: file_id %q does not belong to this skill (cross-skill refs rejected)", args.FileID)
|
||||
}
|
||||
grantedViaDescendant = true
|
||||
}
|
||||
// Scope gate — this is a PUBLICATION primitive (it mints an
|
||||
// unauthenticated link), so it must enforce the same per-user/per-run
|
||||
// scope isolation the read tools do: a same-skill caller must not be
|
||||
// able to publish a file scoped to another user/run. Skipped only for
|
||||
// the descendant-grant case (the worker's file scope is the worker's
|
||||
// run, not the caller's).
|
||||
if !grantedViaDescendant {
|
||||
if err := ValidateScope(inv, meta.Scope, inv.CallerIsAdmin); err != nil {
|
||||
return "", fmt.Errorf("create_file_url: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve expiry.
|
||||
// Resolve expiry. Clamp the caller's seconds BEFORE the multiply so a
|
||||
// huge value can't overflow int64 nanoseconds into a negative
|
||||
// duration that slips under the max-expiry cap (minting an
|
||||
// already-expired token).
|
||||
expiry := DefaultFileURLExpiry
|
||||
if args.ExpiresInSeconds > 0 {
|
||||
expiry = time.Duration(args.ExpiresInSeconds) * time.Second
|
||||
maxSecs := int(MaxFileURLExpiry / time.Second)
|
||||
secs := args.ExpiresInSeconds
|
||||
if secs > maxSecs {
|
||||
secs = maxSecs
|
||||
}
|
||||
expiry = time.Duration(secs) * time.Second
|
||||
}
|
||||
if expiry > MaxFileURLExpiry {
|
||||
expiry = MaxFileURLExpiry
|
||||
@@ -178,7 +201,10 @@ func NewCreateFileURL(minter FileTokenMinter, fileStorage FileStorage, baseURL s
|
||||
if maxViews != nil {
|
||||
res.MaxViews = *maxViews
|
||||
}
|
||||
b, _ := json.Marshal(res)
|
||||
b, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("create_file_url: marshal: %w", err)
|
||||
}
|
||||
return string(b), nil
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user