Browse code

Only use fallback to short IDs when obvious.

As reported in #11294, the Docker daemon will execute contains it
shouldn't run in the event that a requested tag is present in an image's
ID. This leads to the wrong image being started up silently.

This change reduces the risk of such a collision by using the short ID
iff the actual revOrTag looks like a short ID (not that it necessarily
is).

Signed-off-by: Richard Burnison <rburnison@ebay.com>

Richard Burnison authored on 2015/04/11 02:09:08
Showing 3 changed files
... ...
@@ -335,9 +335,12 @@ func (store *TagStore) GetImage(repoName, refOrID string) (*image.Image, error)
335 335
 	}
336 336
 
337 337
 	// If no matching tag is found, search through images for a matching image id
338
-	for _, revision := range repo {
339
-		if strings.HasPrefix(revision, refOrID) {
340
-			return store.graph.Get(revision)
338
+	// iff it looks like a short ID or would look like a short ID
339
+	if stringid.IsShortID(stringid.TruncateID(refOrID)) {
340
+		for _, revision := range repo {
341
+			if strings.HasPrefix(revision, refOrID) {
342
+				return store.graph.Get(revision)
343
+			}
341 344
 		}
342 345
 	}
343 346
 
... ...
@@ -4,19 +4,27 @@ import (
4 4
 	"crypto/rand"
5 5
 	"encoding/hex"
6 6
 	"io"
7
+	"regexp"
7 8
 	"strconv"
8 9
 )
9 10
 
11
+const shortLen = 12
12
+
13
+// Determine if an arbitrary string *looks like* a short ID.
14
+func IsShortID(id string) bool {
15
+	return regexp.MustCompile("^[a-z0-9]{12}$").MatchString(id)
16
+}
17
+
10 18
 // TruncateID returns a shorthand version of a string identifier for convenience.
11 19
 // A collision with other shorthands is very unlikely, but possible.
12 20
 // In case of a collision a lookup with TruncIndex.Get() will fail, and the caller
13 21
 // will need to use a langer prefix, or the full-length Id.
14 22
 func TruncateID(id string) string {
15
-	shortLen := 12
23
+	trimTo := shortLen
16 24
 	if len(id) < shortLen {
17
-		shortLen = len(id)
25
+		trimTo = len(id)
18 26
 	}
19
-	return id[:shortLen]
27
+	return id[:trimTo]
20 28
 }
21 29
 
22 30
 // GenerateRandomID returns an unique id
... ...
@@ -1,6 +1,9 @@
1 1
 package stringid
2 2
 
3
-import "testing"
3
+import (
4
+	"strings"
5
+	"testing"
6
+)
4 7
 
5 8
 func TestGenerateRandomID(t *testing.T) {
6 9
 	id := GenerateRandomID()
... ...
@@ -33,3 +36,21 @@ func TestShortenIdInvalid(t *testing.T) {
33 33
 		t.Fatalf("Id returned is incorrect: truncate on %s returned %s", id, truncID)
34 34
 	}
35 35
 }
36
+
37
+func TestIsShortIDNonHex(t *testing.T) {
38
+	id := "some non-hex value"
39
+	if IsShortID(id) {
40
+		t.Fatalf("%s is not a short ID", id)
41
+	}
42
+}
43
+
44
+func TestIsShortIDNotCorrectSize(t *testing.T) {
45
+	id := strings.Repeat("a", shortLen+1)
46
+	if IsShortID(id) {
47
+		t.Fatalf("%s is not a short ID", id)
48
+	}
49
+	id = strings.Repeat("a", shortLen-1)
50
+	if IsShortID(id) {
51
+		t.Fatalf("%s is not a short ID", id)
52
+	}
53
+}