parseRegistryWarningHeader parses a Warning: header per RFC 7234, limited to the warning values allowed by opencontainers/distribution-spec. It returns the warning string if the header has the expected format, or "" otherwise.
(header string)
| 651 | // values allowed by opencontainers/distribution-spec. |
| 652 | // It returns the warning string if the header has the expected format, or "" otherwise. |
| 653 | func parseRegistryWarningHeader(header string) string { |
| 654 | const expectedPrefix = `299 - "` |
| 655 | const expectedSuffix = `"` |
| 656 | |
| 657 | // warning-value = warn-code SP warn-agent SP warn-text [ SP warn-date ] |
| 658 | // distribution-spec requires warn-code=299, warn-agent="-", warn-date missing |
| 659 | header, ok := strings.CutPrefix(header, expectedPrefix) |
| 660 | if !ok { |
| 661 | return "" |
| 662 | } |
| 663 | header, ok = strings.CutSuffix(header, expectedSuffix) |
| 664 | if !ok { |
| 665 | return "" |
| 666 | } |
| 667 | |
| 668 | // ”Recipients that process the value of a quoted-string MUST handle a quoted-pair |
| 669 | // as if it were replaced by the octet following the backslash.”, so let’s do that… |
| 670 | res := strings.Builder{} |
| 671 | afterBackslash := false |
| 672 | for _, c := range []byte(header) { // []byte because escaping is defined in terms of bytes, not Unicode code points |
| 673 | switch { |
| 674 | case c == 0x7F || (c < ' ' && c != '\t'): |
| 675 | return "" // Control characters are forbidden |
| 676 | case afterBackslash: |
| 677 | res.WriteByte(c) |
| 678 | afterBackslash = false |
| 679 | case c == '"': |
| 680 | // This terminates the warn-text and warn-date, forbidden by distribution-spec, follows, |
| 681 | // or completely invalid input. |
| 682 | return "" |
| 683 | case c == '\\': |
| 684 | afterBackslash = true |
| 685 | default: |
| 686 | res.WriteByte(c) |
| 687 | } |
| 688 | } |
| 689 | if afterBackslash { |
| 690 | return "" |
| 691 | } |
| 692 | return res.String() |
| 693 | } |
| 694 | |
| 695 | // we're using the challenges from the /v2/ ping response and not the one from the destination |
| 696 | // URL in this request because: |
searching dependent graphs…