···33import (
44 "context"
55 "fmt"
66- "strings"
7688- "atcr.io/pkg/atproto"
97 "atcr.io/pkg/hold"
108119 "github.com/spf13/cobra"
1210)
13111414-// Media-type fragments that identify artifact types the scanner intentionally
1515-// skips. Keep this list in sync with scanner/internal/scan/worker.go's
1616-// unscannableConfigTypes — that map keys on config media types; here we look
1717-// at *layer* media types because the backfill walks the hold's layer index
1818-// (which has manifest AT-URIs we can join against scan records).
1919-//
2020-// Detection by layer media type is reliable: helm charts always have a single
2121-// layer with media type application/vnd.cncf.helm.chart.content.v1.tar+gzip;
2222-// in-toto / DSSE attestations use distinct layer types too.
2323-var unscannableLayerMediaSubstrings = []string{
2424- "helm.chart.content",
2525- "in-toto",
2626- "dsse.envelope",
2727-}
2828-2912var scanBackfillConfigFile string
30133114var scanBackfillCmd = &cobra.Command{
3215 Use: "scan-backfill",
3333- Short: "Rewrite legacy scan records to use the status field",
1616+ Short: "Rewrite legacy scan records to use the status field (offline)",
3417 Long: `Walks every io.atcr.hold.scan record on this hold and assigns a status
3518("skipped" or "failed") to records that pre-date the status field.
36193737-A legacy record is one with an empty status, no SBOM blob, and zero vulnerability
3838-counts. The tool inspects each record's manifest's layers to decide:
2020+A legacy record is one with an empty status, no SBOM blob, and zero
2121+vulnerability counts. Layer media types decide the rewrite:
39224040- - layer media type matches helm/in-toto/DSSE → status="skipped"
4141- - everything else → status="failed"
2323+ - helm.chart.content / in-toto / dsse.envelope → status="skipped"
2424+ - everything else → status="failed"
2525+2626+The tool is idempotent and preserves each record's original scannedAt.
42274343-The tool is idempotent: records that already have a status are left alone.
4444-Run once per hold after upgrading.`,
2828+This subcommand opens the hold's CAR store directly, so the running hold
2929+service must be stopped first (otherwise the embedded PDS holds an exclusive
3030+lock). For zero-downtime backfill on a production hold, hit the admin
3131+endpoint POST /admin/api/scan-backfill instead.`,
4532 Args: cobra.NoArgs,
4633 RunE: func(cmd *cobra.Command, args []string) error {
4734 cfg, err := hold.LoadConfig(scanBackfillConfigFile)
···5643 }
5744 defer cleanup()
58455959- ri := holdPDS.RecordsIndex()
6060- if ri == nil {
6161- return fmt.Errorf("records index not available")
4646+ logf := func(format string, args ...any) {
4747+ fmt.Fprintf(cmd.ErrOrStderr(), " "+format+"\n", args...)
6248 }
6363-6464- const batchSize = 200
6565- var (
6666- cursor string
6767- scanned int
6868- rewritten int
6969- markSkipped int
7070- markFailed int
7171- alreadyOK int
7272- )
7373-7474- for {
7575- records, nextCursor, err := ri.ListRecords(atproto.ScanCollection, batchSize, cursor, true)
7676- if err != nil {
7777- return fmt.Errorf("list scan records: %w", err)
7878- }
7979-8080- for _, rec := range records {
8181- scanned++
8282- manifestDigest := "sha256:" + rec.Rkey
8383-8484- _, scanRecord, err := holdPDS.GetScanRecord(ctx, manifestDigest)
8585- if err != nil {
8686- fmt.Fprintf(cmd.ErrOrStderr(), " skip rkey=%s: get failed: %v\n", rec.Rkey, err)
8787- continue
8888- }
8989-9090- // Already classified — nothing to do.
9191- if scanRecord.Status != "" {
9292- alreadyOK++
9393- continue
9494- }
9595-9696- // Only legacy records that signal failure (nil blob + zero
9797- // counts) are candidates. Records with real data don't need
9898- // rewriting; their absent status will be treated as "ok".
9999- if scanRecord.SbomBlob != nil || scanRecord.Total != 0 {
100100- alreadyOK++
101101- continue
102102- }
103103-104104- // Determine artifact type from layer media types.
105105- layers, err := holdPDS.ListLayerRecordsForManifest(ctx, scanRecord.Manifest)
106106- if err != nil {
107107- fmt.Fprintf(cmd.ErrOrStderr(), " skip rkey=%s: list layers failed: %v\n", rec.Rkey, err)
108108- continue
109109- }
110110-111111- skipped := false
112112- for _, l := range layers {
113113- for _, frag := range unscannableLayerMediaSubstrings {
114114- if strings.Contains(l.MediaType, frag) {
115115- skipped = true
116116- break
117117- }
118118- }
119119- if skipped {
120120- break
121121- }
122122- }
123123-124124- var rewrite *atproto.ScanRecord
125125- if skipped {
126126- rewrite = atproto.NewSkippedScanRecord(
127127- manifestDigest,
128128- scanRecord.Repository,
129129- scanRecord.UserDID,
130130- "backfilled: unscannable artifact type",
131131- scanRecord.ScannerVersion,
132132- )
133133- markSkipped++
134134- } else {
135135- rewrite = atproto.NewFailedScanRecord(
136136- manifestDigest,
137137- scanRecord.Repository,
138138- scanRecord.UserDID,
139139- "backfilled: legacy record (no SBOM and zero counts)",
140140- scanRecord.ScannerVersion,
141141- )
142142- markFailed++
143143- }
144144- // Preserve the original ScannedAt — rewriting it would either
145145- // reset the rescan timer or invalidate audit signals.
146146- if scanRecord.ScannedAt != "" {
147147- rewrite.ScannedAt = scanRecord.ScannedAt
148148- }
149149-150150- if _, _, err := holdPDS.CreateScanRecord(ctx, rewrite); err != nil {
151151- fmt.Fprintf(cmd.ErrOrStderr(), " rewrite rkey=%s failed: %v\n", rec.Rkey, err)
152152- continue
153153- }
154154- rewritten++
155155- }
156156-157157- if nextCursor == "" || len(records) == 0 {
158158- break
159159- }
160160- cursor = nextCursor
4949+ res, err := holdPDS.BackfillScanStatus(ctx, logf, nil)
5050+ if err != nil {
5151+ return fmt.Errorf("backfill: %w", err)
16152 }
16253163163- fmt.Fprintf(cmd.OutOrStdout(), "Backfill complete:\n")
164164- fmt.Fprintf(cmd.OutOrStdout(), " scanned: %d\n", scanned)
165165- fmt.Fprintf(cmd.OutOrStdout(), " already-tagged: %d\n", alreadyOK)
166166- fmt.Fprintf(cmd.OutOrStdout(), " → skipped: %d\n", markSkipped)
167167- fmt.Fprintf(cmd.OutOrStdout(), " → failed: %d\n", markFailed)
168168- fmt.Fprintf(cmd.OutOrStdout(), " rewritten: %d\n", rewritten)
5454+ out := cmd.OutOrStdout()
5555+ fmt.Fprintf(out, "Backfill complete:\n")
5656+ fmt.Fprintf(out, " scanned: %d\n", res.Scanned)
5757+ fmt.Fprintf(out, " already-tagged: %d\n", res.AlreadyTagged)
5858+ fmt.Fprintf(out, " → skipped: %d\n", res.MarkedSkipped)
5959+ fmt.Fprintf(out, " → failed: %d\n", res.MarkedFailed)
6060+ fmt.Fprintf(out, " rewritten: %d\n", res.Rewritten)
16961 return nil
17062 },
17163}
···22{{ if eq .Error "upgrade_required" }}
33 <div class="alert alert-info text-sm">
44 {{ icon "sparkles" "size-4" }}
55- <span>AI Image Advisor is a paid feature. <a href="/settings/billing" class="link link-primary font-medium">Upgrade your plan</a> to unlock image analysis.</span>
55+ <span>AI Image Advisor is a paid feature. <a href="/settings/billing" class="link link-hover font-semibold underline">Upgrade your plan</a> to unlock image analysis.</span>
66 </div>
77{{ else if .Error }}
88 <div class="alert alert-warning text-sm">
+163-163
pkg/atproto/cbor_gen.go
···3737 }
38383939 // t.Role (string) (string)
4040- if len("role") > 8192 {
4040+ if len("role") > 1000000 {
4141 return xerrors.Errorf("Value in field \"role\" was too long")
4242 }
4343···4848 return err
4949 }
50505151- if len(t.Role) > 8192 {
5151+ if len(t.Role) > 1000000 {
5252 return xerrors.Errorf("Value in field t.Role was too long")
5353 }
5454···6262 // t.Tier (string) (string)
6363 if t.Tier != "" {
64646565- if len("tier") > 8192 {
6565+ if len("tier") > 1000000 {
6666 return xerrors.Errorf("Value in field \"tier\" was too long")
6767 }
6868···7373 return err
7474 }
75757676- if len(t.Tier) > 8192 {
7676+ if len(t.Tier) > 1000000 {
7777 return xerrors.Errorf("Value in field t.Tier was too long")
7878 }
7979···8686 }
87878888 // t.Type (string) (string)
8989- if len("$type") > 8192 {
8989+ if len("$type") > 1000000 {
9090 return xerrors.Errorf("Value in field \"$type\" was too long")
9191 }
9292···9797 return err
9898 }
9999100100- if len(t.Type) > 8192 {
100100+ if len(t.Type) > 1000000 {
101101 return xerrors.Errorf("Value in field t.Type was too long")
102102 }
103103···109109 }
110110111111 // t.Member (string) (string)
112112- if len("member") > 8192 {
112112+ if len("member") > 1000000 {
113113 return xerrors.Errorf("Value in field \"member\" was too long")
114114 }
115115···120120 return err
121121 }
122122123123- if len(t.Member) > 8192 {
123123+ if len(t.Member) > 1000000 {
124124 return xerrors.Errorf("Value in field t.Member was too long")
125125 }
126126···132132 }
133133134134 // t.AddedAt (string) (string)
135135- if len("addedAt") > 8192 {
135135+ if len("addedAt") > 1000000 {
136136 return xerrors.Errorf("Value in field \"addedAt\" was too long")
137137 }
138138···143143 return err
144144 }
145145146146- if len(t.AddedAt) > 8192 {
146146+ if len(t.AddedAt) > 1000000 {
147147 return xerrors.Errorf("Value in field t.AddedAt was too long")
148148 }
149149···155155 }
156156157157 // t.Plankowner (bool) (bool)
158158- if len("plankowner") > 8192 {
158158+ if len("plankowner") > 1000000 {
159159 return xerrors.Errorf("Value in field \"plankowner\" was too long")
160160 }
161161···171171 }
172172173173 // t.Permissions ([]string) (slice)
174174- if len("permissions") > 8192 {
174174+ if len("permissions") > 1000000 {
175175 return xerrors.Errorf("Value in field \"permissions\" was too long")
176176 }
177177···190190 return err
191191 }
192192 for _, v := range t.Permissions {
193193- if len(v) > 8192 {
193193+ if len(v) > 1000000 {
194194 return xerrors.Errorf("Value in field v was too long")
195195 }
196196···232232233233 nameBuf := make([]byte, 11)
234234 for i := uint64(0); i < n; i++ {
235235- nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192)
235235+ nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000)
236236 if err != nil {
237237 return err
238238 }
···250250 case "role":
251251252252 {
253253- sval, err := cbg.ReadStringWithMax(cr, 8192)
253253+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
254254 if err != nil {
255255 return err
256256 }
···261261 case "tier":
262262263263 {
264264- sval, err := cbg.ReadStringWithMax(cr, 8192)
264264+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
265265 if err != nil {
266266 return err
267267 }
···272272 case "$type":
273273274274 {
275275- sval, err := cbg.ReadStringWithMax(cr, 8192)
275275+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
276276 if err != nil {
277277 return err
278278 }
···283283 case "member":
284284285285 {
286286- sval, err := cbg.ReadStringWithMax(cr, 8192)
286286+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
287287 if err != nil {
288288 return err
289289 }
···294294 case "addedAt":
295295296296 {
297297- sval, err := cbg.ReadStringWithMax(cr, 8192)
297297+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
298298 if err != nil {
299299 return err
300300 }
···349349 _ = err
350350351351 {
352352- sval, err := cbg.ReadStringWithMax(cr, 8192)
352352+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
353353 if err != nil {
354354 return err
355355 }
···392392 }
393393394394 // t.Type (string) (string)
395395- if len("$type") > 8192 {
395395+ if len("$type") > 1000000 {
396396 return xerrors.Errorf("Value in field \"$type\" was too long")
397397 }
398398···403403 return err
404404 }
405405406406- if len(t.Type) > 8192 {
406406+ if len(t.Type) > 1000000 {
407407 return xerrors.Errorf("Value in field t.Type was too long")
408408 }
409409···415415 }
416416417417 // t.Owner (string) (string)
418418- if len("owner") > 8192 {
418418+ if len("owner") > 1000000 {
419419 return xerrors.Errorf("Value in field \"owner\" was too long")
420420 }
421421···426426 return err
427427 }
428428429429- if len(t.Owner) > 8192 {
429429+ if len(t.Owner) > 1000000 {
430430 return xerrors.Errorf("Value in field t.Owner was too long")
431431 }
432432···438438 }
439439440440 // t.Public (bool) (bool)
441441- if len("public") > 8192 {
441441+ if len("public") > 1000000 {
442442 return xerrors.Errorf("Value in field \"public\" was too long")
443443 }
444444···456456 // t.Region (string) (string)
457457 if t.Region != "" {
458458459459- if len("region") > 8192 {
459459+ if len("region") > 1000000 {
460460 return xerrors.Errorf("Value in field \"region\" was too long")
461461 }
462462···467467 return err
468468 }
469469470470- if len(t.Region) > 8192 {
470470+ if len(t.Region) > 1000000 {
471471 return xerrors.Errorf("Value in field t.Region was too long")
472472 }
473473···482482 // t.Successor (string) (string)
483483 if t.Successor != "" {
484484485485- if len("successor") > 8192 {
485485+ if len("successor") > 1000000 {
486486 return xerrors.Errorf("Value in field \"successor\" was too long")
487487 }
488488···493493 return err
494494 }
495495496496- if len(t.Successor) > 8192 {
496496+ if len(t.Successor) > 1000000 {
497497 return xerrors.Errorf("Value in field t.Successor was too long")
498498 }
499499···506506 }
507507508508 // t.DeployedAt (string) (string)
509509- if len("deployedAt") > 8192 {
509509+ if len("deployedAt") > 1000000 {
510510 return xerrors.Errorf("Value in field \"deployedAt\" was too long")
511511 }
512512···517517 return err
518518 }
519519520520- if len(t.DeployedAt) > 8192 {
520520+ if len(t.DeployedAt) > 1000000 {
521521 return xerrors.Errorf("Value in field t.DeployedAt was too long")
522522 }
523523···529529 }
530530531531 // t.AllowAllCrew (bool) (bool)
532532- if len("allowAllCrew") > 8192 {
532532+ if len("allowAllCrew") > 1000000 {
533533 return xerrors.Errorf("Value in field \"allowAllCrew\" was too long")
534534 }
535535···545545 }
546546547547 // t.EnableBlueskyPosts (bool) (bool)
548548- if len("enableBlueskyPosts") > 8192 {
548548+ if len("enableBlueskyPosts") > 1000000 {
549549 return xerrors.Errorf("Value in field \"enableBlueskyPosts\" was too long")
550550 }
551551···589589590590 nameBuf := make([]byte, 18)
591591 for i := uint64(0); i < n; i++ {
592592- nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192)
592592+ nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000)
593593 if err != nil {
594594 return err
595595 }
···607607 case "$type":
608608609609 {
610610- sval, err := cbg.ReadStringWithMax(cr, 8192)
610610+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
611611 if err != nil {
612612 return err
613613 }
···618618 case "owner":
619619620620 {
621621- sval, err := cbg.ReadStringWithMax(cr, 8192)
621621+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
622622 if err != nil {
623623 return err
624624 }
···647647 case "region":
648648649649 {
650650- sval, err := cbg.ReadStringWithMax(cr, 8192)
650650+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
651651 if err != nil {
652652 return err
653653 }
···658658 case "successor":
659659660660 {
661661- sval, err := cbg.ReadStringWithMax(cr, 8192)
661661+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
662662 if err != nil {
663663 return err
664664 }
···669669 case "deployedAt":
670670671671 {
672672- sval, err := cbg.ReadStringWithMax(cr, 8192)
672672+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
673673 if err != nil {
674674 return err
675675 }
···736736 }
737737738738 // t.Size (int64) (int64)
739739- if len("size") > 8192 {
739739+ if len("size") > 1000000 {
740740 return xerrors.Errorf("Value in field \"size\" was too long")
741741 }
742742···758758 }
759759760760 // t.Type (string) (string)
761761- if len("$type") > 8192 {
761761+ if len("$type") > 1000000 {
762762 return xerrors.Errorf("Value in field \"$type\" was too long")
763763 }
764764···769769 return err
770770 }
771771772772- if len(t.Type) > 8192 {
772772+ if len(t.Type) > 1000000 {
773773 return xerrors.Errorf("Value in field t.Type was too long")
774774 }
775775···781781 }
782782783783 // t.Digest (string) (string)
784784- if len("digest") > 8192 {
784784+ if len("digest") > 1000000 {
785785 return xerrors.Errorf("Value in field \"digest\" was too long")
786786 }
787787···792792 return err
793793 }
794794795795- if len(t.Digest) > 8192 {
795795+ if len(t.Digest) > 1000000 {
796796 return xerrors.Errorf("Value in field t.Digest was too long")
797797 }
798798···804804 }
805805806806 // t.UserDID (string) (string)
807807- if len("userDid") > 8192 {
807807+ if len("userDid") > 1000000 {
808808 return xerrors.Errorf("Value in field \"userDid\" was too long")
809809 }
810810···815815 return err
816816 }
817817818818- if len(t.UserDID) > 8192 {
818818+ if len(t.UserDID) > 1000000 {
819819 return xerrors.Errorf("Value in field t.UserDID was too long")
820820 }
821821···827827 }
828828829829 // t.Manifest (string) (string)
830830- if len("manifest") > 8192 {
830830+ if len("manifest") > 1000000 {
831831 return xerrors.Errorf("Value in field \"manifest\" was too long")
832832 }
833833···838838 return err
839839 }
840840841841- if len(t.Manifest) > 8192 {
841841+ if len(t.Manifest) > 1000000 {
842842 return xerrors.Errorf("Value in field t.Manifest was too long")
843843 }
844844···850850 }
851851852852 // t.CreatedAt (string) (string)
853853- if len("createdAt") > 8192 {
853853+ if len("createdAt") > 1000000 {
854854 return xerrors.Errorf("Value in field \"createdAt\" was too long")
855855 }
856856···861861 return err
862862 }
863863864864- if len(t.CreatedAt) > 8192 {
864864+ if len(t.CreatedAt) > 1000000 {
865865 return xerrors.Errorf("Value in field t.CreatedAt was too long")
866866 }
867867···873873 }
874874875875 // t.MediaType (string) (string)
876876- if len("mediaType") > 8192 {
876876+ if len("mediaType") > 1000000 {
877877 return xerrors.Errorf("Value in field \"mediaType\" was too long")
878878 }
879879···884884 return err
885885 }
886886887887- if len(t.MediaType) > 8192 {
887887+ if len(t.MediaType) > 1000000 {
888888 return xerrors.Errorf("Value in field t.MediaType was too long")
889889 }
890890···924924925925 nameBuf := make([]byte, 9)
926926 for i := uint64(0); i < n; i++ {
927927- nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192)
927927+ nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000)
928928 if err != nil {
929929 return err
930930 }
···968968 case "$type":
969969970970 {
971971- sval, err := cbg.ReadStringWithMax(cr, 8192)
971971+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
972972 if err != nil {
973973 return err
974974 }
···979979 case "digest":
980980981981 {
982982- sval, err := cbg.ReadStringWithMax(cr, 8192)
982982+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
983983 if err != nil {
984984 return err
985985 }
···990990 case "userDid":
991991992992 {
993993- sval, err := cbg.ReadStringWithMax(cr, 8192)
993993+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
994994 if err != nil {
995995 return err
996996 }
···10011001 case "manifest":
1002100210031003 {
10041004- sval, err := cbg.ReadStringWithMax(cr, 8192)
10041004+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
10051005 if err != nil {
10061006 return err
10071007 }
···10121012 case "createdAt":
1013101310141014 {
10151015- sval, err := cbg.ReadStringWithMax(cr, 8192)
10151015+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
10161016 if err != nil {
10171017 return err
10181018 }
···10231023 case "mediaType":
1024102410251025 {
10261026- sval, err := cbg.ReadStringWithMax(cr, 8192)
10261026+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
10271027 if err != nil {
10281028 return err
10291029 }
···10541054 }
1055105510561056 // t.Type (string) (string)
10571057- if len("$type") > 8192 {
10571057+ if len("$type") > 1000000 {
10581058 return xerrors.Errorf("Value in field \"$type\" was too long")
10591059 }
10601060···10651065 return err
10661066 }
1067106710681068- if len(t.Type) > 8192 {
10681068+ if len(t.Type) > 1000000 {
10691069 return xerrors.Errorf("Value in field t.Type was too long")
10701070 }
10711071···10771077 }
1078107810791079 // t.Links ([]string) (slice)
10801080- if len("links") > 8192 {
10801080+ if len("links") > 1000000 {
10811081 return xerrors.Errorf("Value in field \"links\" was too long")
10821082 }
10831083···10961096 return err
10971097 }
10981098 for _, v := range t.Links {
10991099- if len(v) > 8192 {
10991099+ if len(v) > 1000000 {
11001100 return xerrors.Errorf("Value in field v was too long")
11011101 }
11021102···11101110 }
1111111111121112 // t.Stats ([]string) (slice)
11131113- if len("stats") > 8192 {
11131113+ if len("stats") > 1000000 {
11141114 return xerrors.Errorf("Value in field \"stats\" was too long")
11151115 }
11161116···11291129 return err
11301130 }
11311131 for _, v := range t.Stats {
11321132- if len(v) > 8192 {
11321132+ if len(v) > 1000000 {
11331133 return xerrors.Errorf("Value in field v was too long")
11341134 }
11351135···11431143 }
1144114411451145 // t.Bluesky (bool) (bool)
11461146- if len("bluesky") > 8192 {
11461146+ if len("bluesky") > 1000000 {
11471147 return xerrors.Errorf("Value in field \"bluesky\" was too long")
11481148 }
11491149···11591159 }
1160116011611161 // t.Location (string) (string)
11621162- if len("location") > 8192 {
11621162+ if len("location") > 1000000 {
11631163 return xerrors.Errorf("Value in field \"location\" was too long")
11641164 }
11651165···11701170 return err
11711171 }
1172117211731173- if len(t.Location) > 8192 {
11731173+ if len(t.Location) > 1000000 {
11741174 return xerrors.Errorf("Value in field t.Location was too long")
11751175 }
11761176···11821182 }
1183118311841184 // t.Description (string) (string)
11851185- if len("description") > 8192 {
11851185+ if len("description") > 1000000 {
11861186 return xerrors.Errorf("Value in field \"description\" was too long")
11871187 }
11881188···11931193 return err
11941194 }
1195119511961196- if len(t.Description) > 8192 {
11961196+ if len(t.Description) > 1000000 {
11971197 return xerrors.Errorf("Value in field t.Description was too long")
11981198 }
11991199···12051205 }
1206120612071207 // t.PinnedRepositories ([]string) (slice)
12081208- if len("pinnedRepositories") > 8192 {
12081208+ if len("pinnedRepositories") > 1000000 {
12091209 return xerrors.Errorf("Value in field \"pinnedRepositories\" was too long")
12101210 }
12111211···12241224 return err
12251225 }
12261226 for _, v := range t.PinnedRepositories {
12271227- if len(v) > 8192 {
12271227+ if len(v) > 1000000 {
12281228 return xerrors.Errorf("Value in field v was too long")
12291229 }
12301230···1266126612671267 nameBuf := make([]byte, 18)
12681268 for i := uint64(0); i < n; i++ {
12691269- nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192)
12691269+ nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000)
12701270 if err != nil {
12711271 return err
12721272 }
···12841284 case "$type":
1285128512861286 {
12871287- sval, err := cbg.ReadStringWithMax(cr, 8192)
12871287+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
12881288 if err != nil {
12891289 return err
12901290 }
···13211321 _ = err
1322132213231323 {
13241324- sval, err := cbg.ReadStringWithMax(cr, 8192)
13241324+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
13251325 if err != nil {
13261326 return err
13271327 }
···13611361 _ = err
1362136213631363 {
13641364- sval, err := cbg.ReadStringWithMax(cr, 8192)
13641364+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
13651365 if err != nil {
13661366 return err
13671367 }
···13931393 case "location":
1394139413951395 {
13961396- sval, err := cbg.ReadStringWithMax(cr, 8192)
13961396+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
13971397 if err != nil {
13981398 return err
13991399 }
···14041404 case "description":
1405140514061406 {
14071407- sval, err := cbg.ReadStringWithMax(cr, 8192)
14071407+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
14081408 if err != nil {
14091409 return err
14101410 }
···14411441 _ = err
1442144214431443 {
14441444- sval, err := cbg.ReadStringWithMax(cr, 8192)
14441444+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
14451445 if err != nil {
14461446 return err
14471447 }
···14841484 }
1485148514861486 // t.Type (string) (string)
14871487- if len("$type") > 8192 {
14871487+ if len("$type") > 1000000 {
14881488 return xerrors.Errorf("Value in field \"$type\" was too long")
14891489 }
14901490···14951495 return err
14961496 }
1497149714981498- if len(t.Type) > 8192 {
14981498+ if len(t.Type) > 1000000 {
14991499 return xerrors.Errorf("Value in field t.Type was too long")
15001500 }
15011501···15091509 // t.LastPull (string) (string)
15101510 if t.LastPull != "" {
1511151115121512- if len("lastPull") > 8192 {
15121512+ if len("lastPull") > 1000000 {
15131513 return xerrors.Errorf("Value in field \"lastPull\" was too long")
15141514 }
15151515···15201520 return err
15211521 }
1522152215231523- if len(t.LastPull) > 8192 {
15231523+ if len(t.LastPull) > 1000000 {
15241524 return xerrors.Errorf("Value in field t.LastPull was too long")
15251525 }
15261526···15351535 // t.LastPush (string) (string)
15361536 if t.LastPush != "" {
1537153715381538- if len("lastPush") > 8192 {
15381538+ if len("lastPush") > 1000000 {
15391539 return xerrors.Errorf("Value in field \"lastPush\" was too long")
15401540 }
15411541···15461546 return err
15471547 }
1548154815491549- if len(t.LastPush) > 8192 {
15491549+ if len(t.LastPush) > 1000000 {
15501550 return xerrors.Errorf("Value in field t.LastPush was too long")
15511551 }
15521552···15591559 }
1560156015611561 // t.OwnerDID (string) (string)
15621562- if len("ownerDid") > 8192 {
15621562+ if len("ownerDid") > 1000000 {
15631563 return xerrors.Errorf("Value in field \"ownerDid\" was too long")
15641564 }
15651565···15701570 return err
15711571 }
1572157215731573- if len(t.OwnerDID) > 8192 {
15731573+ if len(t.OwnerDID) > 1000000 {
15741574 return xerrors.Errorf("Value in field t.OwnerDID was too long")
15751575 }
15761576···15821582 }
1583158315841584 // t.PullCount (int64) (int64)
15851585- if len("pullCount") > 8192 {
15851585+ if len("pullCount") > 1000000 {
15861586 return xerrors.Errorf("Value in field \"pullCount\" was too long")
15871587 }
15881588···16041604 }
1605160516061606 // t.PushCount (int64) (int64)
16071607- if len("pushCount") > 8192 {
16071607+ if len("pushCount") > 1000000 {
16081608 return xerrors.Errorf("Value in field \"pushCount\" was too long")
16091609 }
16101610···16261626 }
1627162716281628 // t.UpdatedAt (string) (string)
16291629- if len("updatedAt") > 8192 {
16291629+ if len("updatedAt") > 1000000 {
16301630 return xerrors.Errorf("Value in field \"updatedAt\" was too long")
16311631 }
16321632···16371637 return err
16381638 }
1639163916401640- if len(t.UpdatedAt) > 8192 {
16401640+ if len(t.UpdatedAt) > 1000000 {
16411641 return xerrors.Errorf("Value in field t.UpdatedAt was too long")
16421642 }
16431643···16491649 }
1650165016511651 // t.Repository (string) (string)
16521652- if len("repository") > 8192 {
16521652+ if len("repository") > 1000000 {
16531653 return xerrors.Errorf("Value in field \"repository\" was too long")
16541654 }
16551655···16601660 return err
16611661 }
1662166216631663- if len(t.Repository) > 8192 {
16631663+ if len(t.Repository) > 1000000 {
16641664 return xerrors.Errorf("Value in field t.Repository was too long")
16651665 }
16661666···1700170017011701 nameBuf := make([]byte, 10)
17021702 for i := uint64(0); i < n; i++ {
17031703- nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192)
17031703+ nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000)
17041704 if err != nil {
17051705 return err
17061706 }
···17181718 case "$type":
1719171917201720 {
17211721- sval, err := cbg.ReadStringWithMax(cr, 8192)
17211721+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
17221722 if err != nil {
17231723 return err
17241724 }
···17291729 case "lastPull":
1730173017311731 {
17321732- sval, err := cbg.ReadStringWithMax(cr, 8192)
17321732+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
17331733 if err != nil {
17341734 return err
17351735 }
···17401740 case "lastPush":
1741174117421742 {
17431743- sval, err := cbg.ReadStringWithMax(cr, 8192)
17431743+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
17441744 if err != nil {
17451745 return err
17461746 }
···17511751 case "ownerDid":
1752175217531753 {
17541754- sval, err := cbg.ReadStringWithMax(cr, 8192)
17541754+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
17551755 if err != nil {
17561756 return err
17571757 }
···18141814 case "updatedAt":
1815181518161816 {
18171817- sval, err := cbg.ReadStringWithMax(cr, 8192)
18171817+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
18181818 if err != nil {
18191819 return err
18201820 }
···18251825 case "repository":
1826182618271827 {
18281828- sval, err := cbg.ReadStringWithMax(cr, 8192)
18281828+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
18291829 if err != nil {
18301830 return err
18311831 }
···18561856 }
1857185718581858 // t.Date (string) (string)
18591859- if len("date") > 8192 {
18591859+ if len("date") > 1000000 {
18601860 return xerrors.Errorf("Value in field \"date\" was too long")
18611861 }
18621862···18671867 return err
18681868 }
1869186918701870- if len(t.Date) > 8192 {
18701870+ if len(t.Date) > 1000000 {
18711871 return xerrors.Errorf("Value in field t.Date was too long")
18721872 }
18731873···18791879 }
1880188018811881 // t.Type (string) (string)
18821882- if len("$type") > 8192 {
18821882+ if len("$type") > 1000000 {
18831883 return xerrors.Errorf("Value in field \"$type\" was too long")
18841884 }
18851885···18901890 return err
18911891 }
1892189218931893- if len(t.Type) > 8192 {
18931893+ if len(t.Type) > 1000000 {
18941894 return xerrors.Errorf("Value in field t.Type was too long")
18951895 }
18961896···19021902 }
1903190319041904 // t.OwnerDID (string) (string)
19051905- if len("ownerDid") > 8192 {
19051905+ if len("ownerDid") > 1000000 {
19061906 return xerrors.Errorf("Value in field \"ownerDid\" was too long")
19071907 }
19081908···19131913 return err
19141914 }
1915191519161916- if len(t.OwnerDID) > 8192 {
19161916+ if len(t.OwnerDID) > 1000000 {
19171917 return xerrors.Errorf("Value in field t.OwnerDID was too long")
19181918 }
19191919···19251925 }
1926192619271927 // t.PullCount (int64) (int64)
19281928- if len("pullCount") > 8192 {
19281928+ if len("pullCount") > 1000000 {
19291929 return xerrors.Errorf("Value in field \"pullCount\" was too long")
19301930 }
19311931···19471947 }
1948194819491949 // t.PushCount (int64) (int64)
19501950- if len("pushCount") > 8192 {
19501950+ if len("pushCount") > 1000000 {
19511951 return xerrors.Errorf("Value in field \"pushCount\" was too long")
19521952 }
19531953···19691969 }
1970197019711971 // t.UpdatedAt (string) (string)
19721972- if len("updatedAt") > 8192 {
19721972+ if len("updatedAt") > 1000000 {
19731973 return xerrors.Errorf("Value in field \"updatedAt\" was too long")
19741974 }
19751975···19801980 return err
19811981 }
1982198219831983- if len(t.UpdatedAt) > 8192 {
19831983+ if len(t.UpdatedAt) > 1000000 {
19841984 return xerrors.Errorf("Value in field t.UpdatedAt was too long")
19851985 }
19861986···19921992 }
1993199319941994 // t.Repository (string) (string)
19951995- if len("repository") > 8192 {
19951995+ if len("repository") > 1000000 {
19961996 return xerrors.Errorf("Value in field \"repository\" was too long")
19971997 }
19981998···20032003 return err
20042004 }
2005200520062006- if len(t.Repository) > 8192 {
20062006+ if len(t.Repository) > 1000000 {
20072007 return xerrors.Errorf("Value in field t.Repository was too long")
20082008 }
20092009···2043204320442044 nameBuf := make([]byte, 10)
20452045 for i := uint64(0); i < n; i++ {
20462046- nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192)
20462046+ nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000)
20472047 if err != nil {
20482048 return err
20492049 }
···20612061 case "date":
2062206220632063 {
20642064- sval, err := cbg.ReadStringWithMax(cr, 8192)
20642064+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
20652065 if err != nil {
20662066 return err
20672067 }
···20722072 case "$type":
2073207320742074 {
20752075- sval, err := cbg.ReadStringWithMax(cr, 8192)
20752075+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
20762076 if err != nil {
20772077 return err
20782078 }
···20832083 case "ownerDid":
2084208420852085 {
20862086- sval, err := cbg.ReadStringWithMax(cr, 8192)
20862086+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
20872087 if err != nil {
20882088 return err
20892089 }
···21462146 case "updatedAt":
2147214721482148 {
21492149- sval, err := cbg.ReadStringWithMax(cr, 8192)
21492149+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
21502150 if err != nil {
21512151 return err
21522152 }
···21572157 case "repository":
2158215821592159 {
21602160- sval, err := cbg.ReadStringWithMax(cr, 8192)
21602160+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
21612161 if err != nil {
21622162 return err
21632163 }
···21972197 }
2198219821992199 // t.Low (int64) (int64)
22002200- if len("low") > 8192 {
22002200+ if len("low") > 1000000 {
22012201 return xerrors.Errorf("Value in field \"low\" was too long")
22022202 }
22032203···22192219 }
2220222022212221 // t.High (int64) (int64)
22222222- if len("high") > 8192 {
22222222+ if len("high") > 1000000 {
22232223 return xerrors.Errorf("Value in field \"high\" was too long")
22242224 }
22252225···22412241 }
2242224222432243 // t.Type (string) (string)
22442244- if len("$type") > 8192 {
22442244+ if len("$type") > 1000000 {
22452245 return xerrors.Errorf("Value in field \"$type\" was too long")
22462246 }
22472247···22522252 return err
22532253 }
2254225422552255- if len(t.Type) > 8192 {
22552255+ if len(t.Type) > 1000000 {
22562256 return xerrors.Errorf("Value in field t.Type was too long")
22572257 }
22582258···22642264 }
2265226522662266 // t.Total (int64) (int64)
22672267- if len("total") > 8192 {
22672267+ if len("total") > 1000000 {
22682268 return xerrors.Errorf("Value in field \"total\" was too long")
22692269 }
22702270···22862286 }
2287228722882288 // t.Medium (int64) (int64)
22892289- if len("medium") > 8192 {
22892289+ if len("medium") > 1000000 {
22902290 return xerrors.Errorf("Value in field \"medium\" was too long")
22912291 }
22922292···23102310 // t.Reason (string) (string)
23112311 if t.Reason != "" {
2312231223132313- if len("reason") > 8192 {
23132313+ if len("reason") > 1000000 {
23142314 return xerrors.Errorf("Value in field \"reason\" was too long")
23152315 }
23162316···23212321 return err
23222322 }
2323232323242324- if len(t.Reason) > 8192 {
23242324+ if len(t.Reason) > 1000000 {
23252325 return xerrors.Errorf("Value in field t.Reason was too long")
23262326 }
23272327···23362336 // t.Status (string) (string)
23372337 if t.Status != "" {
2338233823392339- if len("status") > 8192 {
23392339+ if len("status") > 1000000 {
23402340 return xerrors.Errorf("Value in field \"status\" was too long")
23412341 }
23422342···23472347 return err
23482348 }
2349234923502350- if len(t.Status) > 8192 {
23502350+ if len(t.Status) > 1000000 {
23512351 return xerrors.Errorf("Value in field t.Status was too long")
23522352 }
23532353···23602360 }
2361236123622362 // t.UserDID (string) (string)
23632363- if len("userDid") > 8192 {
23632363+ if len("userDid") > 1000000 {
23642364 return xerrors.Errorf("Value in field \"userDid\" was too long")
23652365 }
23662366···23712371 return err
23722372 }
2373237323742374- if len(t.UserDID) > 8192 {
23742374+ if len(t.UserDID) > 1000000 {
23752375 return xerrors.Errorf("Value in field t.UserDID was too long")
23762376 }
23772377···23832383 }
2384238423852385 // t.Critical (int64) (int64)
23862386- if len("critical") > 8192 {
23862386+ if len("critical") > 1000000 {
23872387 return xerrors.Errorf("Value in field \"critical\" was too long")
23882388 }
23892389···24052405 }
2406240624072407 // t.Manifest (string) (string)
24082408- if len("manifest") > 8192 {
24082408+ if len("manifest") > 1000000 {
24092409 return xerrors.Errorf("Value in field \"manifest\" was too long")
24102410 }
24112411···24162416 return err
24172417 }
2418241824192419- if len(t.Manifest) > 8192 {
24192419+ if len(t.Manifest) > 1000000 {
24202420 return xerrors.Errorf("Value in field t.Manifest was too long")
24212421 }
24222422···24282428 }
2429242924302430 // t.SbomBlob (util.LexBlob) (struct)
24312431- if len("sbomBlob") > 8192 {
24312431+ if len("sbomBlob") > 1000000 {
24322432 return xerrors.Errorf("Value in field \"sbomBlob\" was too long")
24332433 }
24342434···24442444 }
2445244524462446 // t.ScannedAt (string) (string)
24472447- if len("scannedAt") > 8192 {
24472447+ if len("scannedAt") > 1000000 {
24482448 return xerrors.Errorf("Value in field \"scannedAt\" was too long")
24492449 }
24502450···24552455 return err
24562456 }
2457245724582458- if len(t.ScannedAt) > 8192 {
24582458+ if len(t.ScannedAt) > 1000000 {
24592459 return xerrors.Errorf("Value in field t.ScannedAt was too long")
24602460 }
24612461···24672467 }
2468246824692469 // t.Repository (string) (string)
24702470- if len("repository") > 8192 {
24702470+ if len("repository") > 1000000 {
24712471 return xerrors.Errorf("Value in field \"repository\" was too long")
24722472 }
24732473···24782478 return err
24792479 }
2480248024812481- if len(t.Repository) > 8192 {
24812481+ if len(t.Repository) > 1000000 {
24822482 return xerrors.Errorf("Value in field t.Repository was too long")
24832483 }
24842484···24902490 }
2491249124922492 // t.ScannerVersion (string) (string)
24932493- if len("scannerVersion") > 8192 {
24932493+ if len("scannerVersion") > 1000000 {
24942494 return xerrors.Errorf("Value in field \"scannerVersion\" was too long")
24952495 }
24962496···25012501 return err
25022502 }
2503250325042504- if len(t.ScannerVersion) > 8192 {
25042504+ if len(t.ScannerVersion) > 1000000 {
25052505 return xerrors.Errorf("Value in field t.ScannerVersion was too long")
25062506 }
25072507···25132513 }
2514251425152515 // t.VulnReportBlob (util.LexBlob) (struct)
25162516- if len("vulnReportBlob") > 8192 {
25162516+ if len("vulnReportBlob") > 1000000 {
25172517 return xerrors.Errorf("Value in field \"vulnReportBlob\" was too long")
25182518 }
25192519···2557255725582558 nameBuf := make([]byte, 14)
25592559 for i := uint64(0); i < n; i++ {
25602560- nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192)
25602560+ nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000)
25612561 if err != nil {
25622562 return err
25632563 }
···26272627 case "$type":
2628262826292629 {
26302630- sval, err := cbg.ReadStringWithMax(cr, 8192)
26302630+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
26312631 if err != nil {
26322632 return err
26332633 }
···26902690 case "reason":
2691269126922692 {
26932693- sval, err := cbg.ReadStringWithMax(cr, 8192)
26932693+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
26942694 if err != nil {
26952695 return err
26962696 }
···27012701 case "status":
2702270227032703 {
27042704- sval, err := cbg.ReadStringWithMax(cr, 8192)
27042704+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
27052705 if err != nil {
27062706 return err
27072707 }
···27122712 case "userDid":
2713271327142714 {
27152715- sval, err := cbg.ReadStringWithMax(cr, 8192)
27152715+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
27162716 if err != nil {
27172717 return err
27182718 }
···27492749 case "manifest":
2750275027512751 {
27522752- sval, err := cbg.ReadStringWithMax(cr, 8192)
27522752+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
27532753 if err != nil {
27542754 return err
27552755 }
···27802780 case "scannedAt":
2781278127822782 {
27832783- sval, err := cbg.ReadStringWithMax(cr, 8192)
27832783+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
27842784 if err != nil {
27852785 return err
27862786 }
···27912791 case "repository":
2792279227932793 {
27942794- sval, err := cbg.ReadStringWithMax(cr, 8192)
27942794+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
27952795 if err != nil {
27962796 return err
27972797 }
···28022802 case "scannerVersion":
2803280328042804 {
28052805- sval, err := cbg.ReadStringWithMax(cr, 8192)
28052805+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
28062806 if err != nil {
28072807 return err
28082808 }
···28532853 }
2854285428552855 // t.Type (string) (string)
28562856- if len("$type") > 8192 {
28562856+ if len("$type") > 1000000 {
28572857 return xerrors.Errorf("Value in field \"$type\" was too long")
28582858 }
28592859···28642864 return err
28652865 }
2866286628672867- if len(t.Type) > 8192 {
28672867+ if len(t.Type) > 1000000 {
28682868 return xerrors.Errorf("Value in field t.Type was too long")
28692869 }
28702870···28762876 }
2877287728782878 // t.Manifest (string) (string)
28792879- if len("manifest") > 8192 {
28792879+ if len("manifest") > 1000000 {
28802880 return xerrors.Errorf("Value in field \"manifest\" was too long")
28812881 }
28822882···28872887 return err
28882888 }
2889288928902890- if len(t.Manifest) > 8192 {
28902890+ if len(t.Manifest) > 1000000 {
28912891 return xerrors.Errorf("Value in field t.Manifest was too long")
28922892 }
28932893···28992899 }
2900290029012901 // t.CreatedAt (string) (string)
29022902- if len("createdAt") > 8192 {
29022902+ if len("createdAt") > 1000000 {
29032903 return xerrors.Errorf("Value in field \"createdAt\" was too long")
29042904 }
29052905···29102910 return err
29112911 }
2912291229132913- if len(t.CreatedAt) > 8192 {
29132913+ if len(t.CreatedAt) > 1000000 {
29142914 return xerrors.Errorf("Value in field t.CreatedAt was too long")
29152915 }
29162916···29222922 }
2923292329242924 // t.ConfigJSON (string) (string)
29252925- if len("configJson") > 8192 {
29252925+ if len("configJson") > 1000000 {
29262926 return xerrors.Errorf("Value in field \"configJson\" was too long")
29272927 }
29282928···29332933 return err
29342934 }
2935293529362936- if len(t.ConfigJSON) > 8192 {
29362936+ if len(t.ConfigJSON) > 1000000 {
29372937 return xerrors.Errorf("Value in field t.ConfigJSON was too long")
29382938 }
29392939···2973297329742974 nameBuf := make([]byte, 10)
29752975 for i := uint64(0); i < n; i++ {
29762976- nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192)
29762976+ nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000)
29772977 if err != nil {
29782978 return err
29792979 }
···29912991 case "$type":
2992299229932993 {
29942994- sval, err := cbg.ReadStringWithMax(cr, 8192)
29942994+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
29952995 if err != nil {
29962996 return err
29972997 }
···30023002 case "manifest":
3003300330043004 {
30053005- sval, err := cbg.ReadStringWithMax(cr, 8192)
30053005+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
30063006 if err != nil {
30073007 return err
30083008 }
···30133013 case "createdAt":
3014301430153015 {
30163016- sval, err := cbg.ReadStringWithMax(cr, 8192)
30163016+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
30173017 if err != nil {
30183018 return err
30193019 }
···30243024 case "configJson":
3025302530263026 {
30273027- sval, err := cbg.ReadStringWithMax(cr, 8192)
30273027+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
30283028 if err != nil {
30293029 return err
30303030 }
+9-2
pkg/atproto/generate.go
···2525)
26262727func main() {
2828- // Generate map-style encoders
2929- if err := cbg.WriteMapEncodersToFile("cbor_gen.go", "atproto",
2828+ // MaxStringLength bumps the package-wide string read limit. cbor-gen v0.3.1
2929+ // honors per-field `cborgen:"...,maxlen=N"` tags only on the write side; the
3030+ // unmarshal template hard-codes the package default. ImageConfigRecord's
3131+ // configJson holds raw OCI image configs that routinely exceed the stock
3232+ // 8KB default (deep build histories, verbose created_by lines), so we lift
3333+ // the ceiling for everyone. Per-field write caps still apply.
3434+ gen := cbg.Gen{MaxStringLength: 1_000_000}
3535+3636+ if err := gen.WriteMapEncodersToFile("cbor_gen.go", "atproto",
3037 atproto.CrewRecord{},
3138 atproto.CaptainRecord{},
3239 atproto.LayerRecord{},
+3-3
pkg/atproto/lexicon.go
···932932// Stores the full OCI config JSON so the appview can display layer history including empty layers
933933type ImageConfigRecord struct {
934934 Type string `json:"$type" cborgen:"$type"`
935935- Manifest string `json:"manifest" cborgen:"manifest"` // AT-URI of the manifest
936936- ConfigJSON string `json:"configJson" cborgen:"configJson"` // Raw OCI image config JSON
937937- CreatedAt string `json:"createdAt" cborgen:"createdAt"` // RFC3339 timestamp
935935+ Manifest string `json:"manifest" cborgen:"manifest"` // AT-URI of the manifest
936936+ ConfigJSON string `json:"configJson" cborgen:"configJson,maxlen=1000000"` // Raw OCI image config JSON. Cap mirrors lexicon maxLength so deep image histories (Bazel, multi-stage builds with verbose created_by lines) fit.
937937+ CreatedAt string `json:"createdAt" cborgen:"createdAt"` // RFC3339 timestamp
938938}
939939940940// NewImageConfigRecord creates a new image config record
+70
pkg/atproto/lexicon_test.go
···11package atproto
2233import (
44+ "bytes"
45 "encoding/json"
56 "strings"
67 "testing"
···13851386 t.Errorf("Avatar.Ref.Link = %v, want %v", decoded.Avatar.Ref.Link, record.Avatar.Ref.Link)
13861387 }
13871388}
13891389+13901390+// TestImageConfigRecord_CBORRoundTrip locks in that we can encode and decode
13911391+// large OCI image configs without hitting cbor-gen's default 8KB string cap.
13921392+// Real-world images with deep build histories (Bazel, multi-stage Dockerfiles)
13931393+// routinely produce config blobs that blow past the default; if either side
13941394+// regresses, the backfill silently drops records with "configJson was too
13951395+// long" instead of populating the layer-history UI.
13961396+func TestImageConfigRecord_CBORRoundTrip(t *testing.T) {
13971397+ tests := []struct {
13981398+ name string
13991399+ payloadSize int
14001400+ }{
14011401+ // Small payloads exercise the happy path — should always have worked.
14021402+ {"small", 1024},
14031403+ // 16KB is well past the old 8192 cborgen default. Pre-fix this would
14041404+ // have failed at marshal time.
14051405+ {"medium-16kb", 16 * 1024},
14061406+ // 200KB matches the upper end of pathological real configs (deep
14071407+ // Bazel histories with verbose created_by lines).
14081408+ {"large-200kb", 200 * 1024},
14091409+ // 900KB is just under our 1MB cap — the read side previously
14101410+ // hard-coded 8192 even with the per-field write tag, so this also
14111411+ // guards against the unmarshal regression.
14121412+ {"near-cap-900kb", 900 * 1024},
14131413+ }
14141414+14151415+ for _, tc := range tests {
14161416+ t.Run(tc.name, func(t *testing.T) {
14171417+ // Use a repeating non-trivial pattern so any byte-level corruption
14181418+ // in encode/decode shows up as a mismatch rather than blending
14191419+ // into a sea of identical bytes.
14201420+ const chunk = "history entry: bazel build //pkg:foo # "
14211421+ payload := strings.Repeat(chunk, tc.payloadSize/len(chunk)+1)[:tc.payloadSize]
14221422+14231423+ record := &ImageConfigRecord{
14241424+ Type: ImageConfigCollection,
14251425+ Manifest: "at://did:plc:test/io.atcr.manifest/abc",
14261426+ ConfigJSON: payload,
14271427+ CreatedAt: time.Now().UTC().Format(time.RFC3339),
14281428+ }
14291429+14301430+ var buf bytes.Buffer
14311431+ if err := record.MarshalCBOR(&buf); err != nil {
14321432+ t.Fatalf("MarshalCBOR(%d bytes): %v", tc.payloadSize, err)
14331433+ }
14341434+14351435+ var decoded ImageConfigRecord
14361436+ if err := decoded.UnmarshalCBOR(&buf); err != nil {
14371437+ t.Fatalf("UnmarshalCBOR(%d bytes): %v", tc.payloadSize, err)
14381438+ }
14391439+14401440+ if decoded.Type != record.Type {
14411441+ t.Errorf("Type = %q, want %q", decoded.Type, record.Type)
14421442+ }
14431443+ if decoded.Manifest != record.Manifest {
14441444+ t.Errorf("Manifest = %q, want %q", decoded.Manifest, record.Manifest)
14451445+ }
14461446+ if decoded.CreatedAt != record.CreatedAt {
14471447+ t.Errorf("CreatedAt = %q, want %q", decoded.CreatedAt, record.CreatedAt)
14481448+ }
14491449+ if len(decoded.ConfigJSON) != len(record.ConfigJSON) {
14501450+ t.Fatalf("ConfigJSON length = %d, want %d", len(decoded.ConfigJSON), len(record.ConfigJSON))
14511451+ }
14521452+ if decoded.ConfigJSON != record.ConfigJSON {
14531453+ t.Errorf("ConfigJSON content mismatch at length %d", len(record.ConfigJSON))
14541454+ }
14551455+ })
14561456+ }
14571457+}
+21
pkg/hold/admin/admin.go
···9090 // In-memory session storage (single user, no persistence needed)
9191 sessions map[string]*AdminSession
9292 sessionsMu sync.RWMutex
9393+9494+ // scan-backfill state — runs as a background goroutine on click; the
9595+ // status endpoint reads this for progress polling. Only one run at a
9696+ // time (idempotent, so re-running is safe but pointless).
9797+ scanBackfill scanBackfillState
9898+}
9999+100100+// scanBackfillState tracks the in-flight scan-status backfill run.
101101+type scanBackfillState struct {
102102+ mu sync.Mutex
103103+ running bool
104104+ startedAt time.Time
105105+ current *pds.ScanBackfillResult // running totals (snapshot)
106106+ result *pds.ScanBackfillResult // final result, set when running=false
107107+ err string // last error (running ends with err set)
93108}
9410995110// adminContextKey is used to store session data in request context
···531546 r.Get("/admin/api/top-users", ui.handleTopUsersAPI)
532547 r.Get("/admin/api/relay/status", ui.handleRelayStatus)
533548 r.Get("/admin/api/crew/member", ui.handleCrewMemberInfo)
549549+550550+ // Scan-record backfill: kicks off a background run and returns a
551551+ // progress fragment that polls /status. Use Accept:application/json
552552+ // for a synchronous JSON response (curl-friendly).
553553+ r.Post("/admin/api/scan-backfill", ui.handleScanBackfill)
554554+ r.Get("/admin/api/scan-backfill/status", ui.handleScanBackfillStatus)
534555535556 // Logout
536557 r.Post("/admin/auth/logout", ui.handleLogout)
+173
pkg/hold/admin/handlers_scan.go
···11+package admin
22+33+import (
44+ "context"
55+ "encoding/json"
66+ "fmt"
77+ "log/slog"
88+ "net/http"
99+ "strings"
1010+ "time"
1111+1212+ "atcr.io/pkg/hold/pds"
1313+)
1414+1515+// handleScanBackfill kicks off a scan-status backfill in a background
1616+// goroutine and returns a progress fragment that polls
1717+// /admin/api/scan-backfill/status for updates. Idempotent — clicking again
1818+// while a run is in flight just shows the current progress.
1919+//
2020+// Why background: reverse proxies typically cap upstream HTTP timeouts at
2121+// 10–60s, which would cancel a synchronous request mid-loop. Detaching the
2222+// work from the request context lets it run to completion.
2323+//
2424+// JSON callers (Accept: application/json) get a synchronous run instead —
2525+// useful for curl + scripting.
2626+func (ui *AdminUI) handleScanBackfill(w http.ResponseWriter, r *http.Request) {
2727+ session := getSessionFromContext(r.Context())
2828+ wantJSON := strings.Contains(r.Header.Get("Accept"), "application/json")
2929+3030+ if wantJSON {
3131+ // Synchronous JSON path — caller decides their own timeout.
3232+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
3333+ defer cancel()
3434+ res, err := ui.pds.BackfillScanStatus(ctx, scanBackfillLogger, nil)
3535+ if err != nil {
3636+ slog.Error("scan-backfill failed", "by", session.DID, "error", err)
3737+ http.Error(w, err.Error(), http.StatusInternalServerError)
3838+ return
3939+ }
4040+ slog.Info("scan-status backfill complete (sync)",
4141+ "by", session.DID,
4242+ "scanned", res.Scanned,
4343+ "already_tagged", res.AlreadyTagged,
4444+ "marked_skipped", res.MarkedSkipped,
4545+ "marked_failed", res.MarkedFailed,
4646+ "rewritten", res.Rewritten,
4747+ )
4848+ w.Header().Set("Content-Type", "application/json")
4949+ _ = json.NewEncoder(w).Encode(res)
5050+ return
5151+ }
5252+5353+ // HTML path — kick off the background run if one isn't already going.
5454+ st := &ui.scanBackfill
5555+ st.mu.Lock()
5656+ alreadyRunning := st.running
5757+ if !alreadyRunning {
5858+ st.running = true
5959+ st.startedAt = time.Now()
6060+ st.current = &pds.ScanBackfillResult{}
6161+ st.result = nil
6262+ st.err = ""
6363+ }
6464+ st.mu.Unlock()
6565+6666+ if !alreadyRunning {
6767+ slog.Info("scan-status backfill started via admin panel", "by", session.DID)
6868+ go ui.runScanBackfill()
6969+ } else {
7070+ slog.Debug("scan-status backfill already in progress; returning current state")
7171+ }
7272+7373+ ui.renderTemplate(w, "partials/scan_backfill_progress.html", ui.snapshotScanBackfill())
7474+}
7575+7676+// handleScanBackfillStatus is polled by the progress fragment. Returns the
7777+// progress fragment again if running, the result fragment when done, or an
7878+// error fragment if something went wrong.
7979+func (ui *AdminUI) handleScanBackfillStatus(w http.ResponseWriter, r *http.Request) {
8080+ snap := ui.snapshotScanBackfill()
8181+ if snap.Running {
8282+ ui.renderTemplate(w, "partials/scan_backfill_progress.html", snap)
8383+ return
8484+ }
8585+ if snap.Error != "" {
8686+ ui.renderTemplate(w, "partials/gc_error.html", struct{ Error string }{snap.Error})
8787+ return
8888+ }
8989+ if snap.Result == nil {
9090+ // Initial state, before any run — render an empty placeholder.
9191+ _, _ = w.Write([]byte(""))
9292+ return
9393+ }
9494+ ui.renderTemplate(w, "partials/scan_backfill_result.html", snap.Result)
9595+}
9696+9797+// runScanBackfill is the goroutine body. Updates the shared state as the
9898+// backfill progresses and stores the final result (or error) when it ends.
9999+func (ui *AdminUI) runScanBackfill() {
100100+ st := &ui.scanBackfill
101101+ defer func() {
102102+ st.mu.Lock()
103103+ st.running = false
104104+ st.mu.Unlock()
105105+ }()
106106+107107+ // Generous independent timeout — the loop is single-threaded and large
108108+ // holds with thousands of legacy records can take a few minutes.
109109+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
110110+ defer cancel()
111111+112112+ res, err := ui.pds.BackfillScanStatus(ctx, scanBackfillLogger, func(snap *pds.ScanBackfillResult) {
113113+ // Copy so we don't keep a pointer the loop will mutate.
114114+ c := *snap
115115+ st.mu.Lock()
116116+ st.current = &c
117117+ st.mu.Unlock()
118118+ })
119119+ st.mu.Lock()
120120+ if err != nil {
121121+ st.err = err.Error()
122122+ slog.Error("scan-status backfill failed", "error", err)
123123+ } else {
124124+ st.result = res
125125+ slog.Info("scan-status backfill complete",
126126+ "scanned", res.Scanned,
127127+ "already_tagged", res.AlreadyTagged,
128128+ "marked_skipped", res.MarkedSkipped,
129129+ "marked_failed", res.MarkedFailed,
130130+ "rewritten", res.Rewritten,
131131+ )
132132+ }
133133+ st.mu.Unlock()
134134+}
135135+136136+// scanBackfillSnapshot is the shape exposed to templates.
137137+type scanBackfillSnapshot struct {
138138+ Running bool
139139+ StartedAt time.Time
140140+ Current *pds.ScanBackfillResult // populated while running
141141+ Result *pds.ScanBackfillResult // populated when complete
142142+ Error string
143143+}
144144+145145+// snapshotScanBackfill returns a copy of the current state — safe to render
146146+// without holding the mutex.
147147+func (ui *AdminUI) snapshotScanBackfill() scanBackfillSnapshot {
148148+ st := &ui.scanBackfill
149149+ st.mu.Lock()
150150+ defer st.mu.Unlock()
151151+152152+ snap := scanBackfillSnapshot{
153153+ Running: st.running,
154154+ StartedAt: st.startedAt,
155155+ Error: st.err,
156156+ }
157157+ if st.current != nil {
158158+ c := *st.current
159159+ snap.Current = &c
160160+ }
161161+ if st.result != nil {
162162+ r := *st.result
163163+ snap.Result = &r
164164+ }
165165+ return snap
166166+}
167167+168168+// scanBackfillLogger formats the printf-style messages from BackfillScanStatus
169169+// into a single slog message — slog's variadic args are key/value pairs, not
170170+// printf operands.
171171+func scanBackfillLogger(format string, args ...any) {
172172+ slog.Warn("scan-backfill: " + fmt.Sprintf(format, args...))
173173+}
···33import (
44 "context"
55 "fmt"
66+ "strings"
6778 "atcr.io/pkg/atproto"
89 "github.com/ipfs/go-cid"
910)
1111+1212+// unscannableLayerMediaSubstrings lists layer media-type fragments that
1313+// identify artifact types the scanner intentionally skips. Kept in sync with
1414+// scanner/internal/scan/worker.go's unscannableConfigTypes — that map keys on
1515+// config media types; here we look at *layer* media types because the
1616+// backfill walks the hold's layer index (manifest AT-URIs join layers to
1717+// scan records).
1818+var unscannableLayerMediaSubstrings = []string{
1919+ "helm.chart.content",
2020+ "in-toto",
2121+ "dsse.envelope",
2222+}
2323+2424+// ScanBackfillResult summarizes what BackfillScanStatus did.
2525+type ScanBackfillResult struct {
2626+ Scanned int // total scan records examined
2727+ AlreadyTagged int // records with non-empty status (or non-failure shape) — left alone
2828+ MarkedSkipped int // records rewritten as status=skipped
2929+ MarkedFailed int // records rewritten as status=failed
3030+ Rewritten int // total rewrites (MarkedSkipped + MarkedFailed) that succeeded
3131+}
3232+3333+// BackfillScanStatus walks every io.atcr.hold.scan record on this hold and
3434+// assigns a status ("skipped" or "failed") to legacy records that pre-date the
3535+// status field. Idempotent — records that already have a non-empty status are
3636+// left alone.
3737+//
3838+// A legacy record is one with empty status, no SBOM blob, and zero
3939+// vulnerability counts. Layer media types are inspected to choose the
4040+// rewrite: helm/in-toto/DSSE → skipped, otherwise → failed. ScannedAt is
4141+// preserved on rewrite so the rescan timer doesn't reset.
4242+//
4343+// Safe to call on a running hold — uses the existing repomgr APIs the same
4444+// way scan-job handling does.
4545+//
4646+// progress, if non-nil, is called periodically with a snapshot of the
4747+// running totals so callers can surface progress to the UI. The callback
4848+// must not retain the pointer past its return.
4949+func (p *HoldPDS) BackfillScanStatus(ctx context.Context, log func(format string, args ...any), progress func(*ScanBackfillResult)) (*ScanBackfillResult, error) {
5050+ if log == nil {
5151+ log = func(string, ...any) {}
5252+ }
5353+ if progress == nil {
5454+ progress = func(*ScanBackfillResult) {}
5555+ }
5656+ ri := p.RecordsIndex()
5757+ if ri == nil {
5858+ return nil, fmt.Errorf("records index not available")
5959+ }
6060+6161+ const batchSize = 200
6262+ res := &ScanBackfillResult{}
6363+ var cursor string
6464+6565+ for {
6666+ records, nextCursor, err := ri.ListRecords(atproto.ScanCollection, batchSize, cursor, true)
6767+ if err != nil {
6868+ return res, fmt.Errorf("list scan records: %w", err)
6969+ }
7070+7171+ for _, rec := range records {
7272+ if err := ctx.Err(); err != nil {
7373+ return res, err
7474+ }
7575+ res.Scanned++
7676+ manifestDigest := "sha256:" + rec.Rkey
7777+7878+ _, scanRecord, err := p.GetScanRecord(ctx, manifestDigest)
7979+ if err != nil {
8080+ log("skip rkey=%s: get failed: %v", rec.Rkey, err)
8181+ continue
8282+ }
8383+8484+ // Already classified — nothing to do.
8585+ if scanRecord.Status != "" {
8686+ res.AlreadyTagged++
8787+ continue
8888+ }
8989+ // Real data present (legacy successful scan) — treat as ok, don't rewrite.
9090+ if scanRecord.SbomBlob != nil || scanRecord.Total != 0 {
9191+ res.AlreadyTagged++
9292+ continue
9393+ }
9494+9595+ // Determine artifact type from layer media types.
9696+ layers, err := p.ListLayerRecordsForManifest(ctx, scanRecord.Manifest)
9797+ if err != nil {
9898+ log("skip rkey=%s: list layers failed: %v", rec.Rkey, err)
9999+ continue
100100+ }
101101+102102+ skipped := false
103103+ for _, l := range layers {
104104+ for _, frag := range unscannableLayerMediaSubstrings {
105105+ if strings.Contains(l.MediaType, frag) {
106106+ skipped = true
107107+ break
108108+ }
109109+ }
110110+ if skipped {
111111+ break
112112+ }
113113+ }
114114+115115+ var rewrite *atproto.ScanRecord
116116+ if skipped {
117117+ rewrite = atproto.NewSkippedScanRecord(
118118+ manifestDigest,
119119+ scanRecord.Repository,
120120+ scanRecord.UserDID,
121121+ "backfilled: unscannable artifact type",
122122+ scanRecord.ScannerVersion,
123123+ )
124124+ res.MarkedSkipped++
125125+ } else {
126126+ rewrite = atproto.NewFailedScanRecord(
127127+ manifestDigest,
128128+ scanRecord.Repository,
129129+ scanRecord.UserDID,
130130+ "backfilled: legacy record (no SBOM and zero counts)",
131131+ scanRecord.ScannerVersion,
132132+ )
133133+ res.MarkedFailed++
134134+ }
135135+ // Preserve original ScannedAt so the rescan timer doesn't reset
136136+ // and we don't lose audit signal.
137137+ if scanRecord.ScannedAt != "" {
138138+ rewrite.ScannedAt = scanRecord.ScannedAt
139139+ }
140140+141141+ if _, _, err := p.CreateScanRecord(ctx, rewrite); err != nil {
142142+ log("rewrite rkey=%s failed: %v", rec.Rkey, err)
143143+ continue
144144+ }
145145+ res.Rewritten++
146146+ }
147147+148148+ // Report progress at every batch boundary — gives the UI smooth-ish
149149+ // updates without locking the loop on every record.
150150+ progress(res)
151151+152152+ if nextCursor == "" || len(records) == 0 {
153153+ break
154154+ }
155155+ cursor = nextCursor
156156+ }
157157+158158+ progress(res)
159159+ return res, nil
160160+}
1016111162// CreateScanRecord creates or updates a scan result record in the hold's PDS
12163// Uses a deterministic rkey based on the manifest digest, so re-scans upsert