Review: one local name is still a local name; a local name is unique; recovery knows it; recipes read as instructions; a tag before a digest; ask fails at once when nothing serves
A secrets object with one local name delivered no file. Two requirements could share a local name. secret recover and the export could not tell two locals apart. The recipe check missed continued lines and read heredoc bodies as bases. repo:tag@digest kept the tag in the repository. ask now publishes mandatory, so a tool nothing serves is said at once rather than after the wait.
This commit is contained in:
@@ -831,11 +831,7 @@ func undeclaredFetches(recipe string, declared map[string]bool) (bases, copies [
|
||||
*out = append(*out, ref)
|
||||
}
|
||||
}
|
||||
for _, raw := range strings.Split(recipe, "\n") {
|
||||
line := strings.TrimSpace(raw)
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
for _, line := range instructions(recipe) {
|
||||
fields := strings.Fields(line)
|
||||
switch strings.ToUpper(fields[0]) {
|
||||
case "FROM":
|
||||
@@ -860,7 +856,64 @@ func undeclaredFetches(recipe string, declared map[string]bool) (bases, copies [
|
||||
note(strings.TrimPrefix(f, "--from="))
|
||||
}
|
||||
}
|
||||
case "RUN":
|
||||
// RUN --mount=type=bind,from=<image>,… reaches for an image exactly as COPY --from does.
|
||||
out = &copies
|
||||
for _, f := range fields[1:] {
|
||||
if !strings.HasPrefix(f, "--mount=") {
|
||||
continue
|
||||
}
|
||||
for _, opt := range strings.Split(strings.TrimPrefix(f, "--mount="), ",") {
|
||||
if from, found := strings.CutPrefix(opt, "from="); found {
|
||||
note(from)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return bases, copies
|
||||
}
|
||||
|
||||
// instructions is a recipe as its instructions, one per line: continuations joined, comments and
|
||||
// blank lines dropped, and heredoc bodies (`COPY <<EOF … EOF`) skipped — a Python file written into
|
||||
// an image is not a list of images to fetch. The review found a `COPY \` continued onto the next
|
||||
// line slip past the check, and a stage named on a continuation line refused as a fetch.
|
||||
func instructions(recipe string) []string {
|
||||
var out []string
|
||||
var current strings.Builder
|
||||
var heredoc string
|
||||
flush := func() {
|
||||
if line := strings.TrimSpace(current.String()); line != "" && !strings.HasPrefix(line, "#") {
|
||||
out = append(out, line)
|
||||
}
|
||||
current.Reset()
|
||||
}
|
||||
for _, raw := range strings.Split(recipe, "\n") {
|
||||
if heredoc != "" {
|
||||
if strings.TrimSpace(raw) == heredoc {
|
||||
heredoc = ""
|
||||
}
|
||||
continue
|
||||
}
|
||||
line := strings.TrimRight(raw, " \t")
|
||||
if strings.HasPrefix(strings.TrimSpace(line), "#") && current.Len() == 0 {
|
||||
continue
|
||||
}
|
||||
if strings.HasSuffix(line, "\\") {
|
||||
current.WriteString(strings.TrimSuffix(line, "\\"))
|
||||
current.WriteString(" ")
|
||||
continue
|
||||
}
|
||||
current.WriteString(line)
|
||||
if at := strings.Index(current.String(), "<<"); at >= 0 {
|
||||
// `<<EOF`, `<<-EOF`, `<<'EOF'`, `<<"EOF"`: the body runs to a line that is the word.
|
||||
word := strings.Fields(current.String()[at+2:])
|
||||
if len(word) > 0 {
|
||||
heredoc = strings.Trim(strings.TrimPrefix(word[0], "-"), `'"`)
|
||||
}
|
||||
}
|
||||
flush()
|
||||
}
|
||||
flush()
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user