diff_test.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. package archive // import "github.com/docker/docker/pkg/archive"
  2. import (
  3. "archive/tar"
  4. "io"
  5. "os"
  6. "path/filepath"
  7. "reflect"
  8. "testing"
  9. "github.com/docker/docker/pkg/ioutils"
  10. )
  11. func TestApplyLayerInvalidFilenames(t *testing.T) {
  12. for i, headers := range [][]*tar.Header{
  13. {
  14. {
  15. Name: "../victim/dotdot",
  16. Typeflag: tar.TypeReg,
  17. Mode: 0o644,
  18. },
  19. },
  20. {
  21. {
  22. // Note the leading slash
  23. Name: "/../victim/slash-dotdot",
  24. Typeflag: tar.TypeReg,
  25. Mode: 0o644,
  26. },
  27. },
  28. } {
  29. if err := testBreakout("applylayer", "docker-TestApplyLayerInvalidFilenames", headers); err != nil {
  30. t.Fatalf("i=%d. %v", i, err)
  31. }
  32. }
  33. }
  34. func TestApplyLayerInvalidHardlink(t *testing.T) {
  35. for i, headers := range [][]*tar.Header{
  36. { // try reading victim/hello (../)
  37. {
  38. Name: "dotdot",
  39. Typeflag: tar.TypeLink,
  40. Linkname: "../victim/hello",
  41. Mode: 0o644,
  42. },
  43. },
  44. { // try reading victim/hello (/../)
  45. {
  46. Name: "slash-dotdot",
  47. Typeflag: tar.TypeLink,
  48. // Note the leading slash
  49. Linkname: "/../victim/hello",
  50. Mode: 0o644,
  51. },
  52. },
  53. { // try writing victim/file
  54. {
  55. Name: "loophole-victim",
  56. Typeflag: tar.TypeLink,
  57. Linkname: "../victim",
  58. Mode: 0o755,
  59. },
  60. {
  61. Name: "loophole-victim/file",
  62. Typeflag: tar.TypeReg,
  63. Mode: 0o644,
  64. },
  65. },
  66. { // try reading victim/hello (hardlink, symlink)
  67. {
  68. Name: "loophole-victim",
  69. Typeflag: tar.TypeLink,
  70. Linkname: "../victim",
  71. Mode: 0o755,
  72. },
  73. {
  74. Name: "symlink",
  75. Typeflag: tar.TypeSymlink,
  76. Linkname: "loophole-victim/hello",
  77. Mode: 0o644,
  78. },
  79. },
  80. { // Try reading victim/hello (hardlink, hardlink)
  81. {
  82. Name: "loophole-victim",
  83. Typeflag: tar.TypeLink,
  84. Linkname: "../victim",
  85. Mode: 0o755,
  86. },
  87. {
  88. Name: "hardlink",
  89. Typeflag: tar.TypeLink,
  90. Linkname: "loophole-victim/hello",
  91. Mode: 0o644,
  92. },
  93. },
  94. { // Try removing victim directory (hardlink)
  95. {
  96. Name: "loophole-victim",
  97. Typeflag: tar.TypeLink,
  98. Linkname: "../victim",
  99. Mode: 0o755,
  100. },
  101. {
  102. Name: "loophole-victim",
  103. Typeflag: tar.TypeReg,
  104. Mode: 0o644,
  105. },
  106. },
  107. } {
  108. if err := testBreakout("applylayer", "docker-TestApplyLayerInvalidHardlink", headers); err != nil {
  109. t.Fatalf("i=%d. %v", i, err)
  110. }
  111. }
  112. }
  113. func TestApplyLayerInvalidSymlink(t *testing.T) {
  114. for i, headers := range [][]*tar.Header{
  115. { // try reading victim/hello (../)
  116. {
  117. Name: "dotdot",
  118. Typeflag: tar.TypeSymlink,
  119. Linkname: "../victim/hello",
  120. Mode: 0o644,
  121. },
  122. },
  123. { // try reading victim/hello (/../)
  124. {
  125. Name: "slash-dotdot",
  126. Typeflag: tar.TypeSymlink,
  127. // Note the leading slash
  128. Linkname: "/../victim/hello",
  129. Mode: 0o644,
  130. },
  131. },
  132. { // try writing victim/file
  133. {
  134. Name: "loophole-victim",
  135. Typeflag: tar.TypeSymlink,
  136. Linkname: "../victim",
  137. Mode: 0o755,
  138. },
  139. {
  140. Name: "loophole-victim/file",
  141. Typeflag: tar.TypeReg,
  142. Mode: 0o644,
  143. },
  144. },
  145. { // try reading victim/hello (symlink, symlink)
  146. {
  147. Name: "loophole-victim",
  148. Typeflag: tar.TypeSymlink,
  149. Linkname: "../victim",
  150. Mode: 0o755,
  151. },
  152. {
  153. Name: "symlink",
  154. Typeflag: tar.TypeSymlink,
  155. Linkname: "loophole-victim/hello",
  156. Mode: 0o644,
  157. },
  158. },
  159. { // try reading victim/hello (symlink, hardlink)
  160. {
  161. Name: "loophole-victim",
  162. Typeflag: tar.TypeSymlink,
  163. Linkname: "../victim",
  164. Mode: 0o755,
  165. },
  166. {
  167. Name: "hardlink",
  168. Typeflag: tar.TypeLink,
  169. Linkname: "loophole-victim/hello",
  170. Mode: 0o644,
  171. },
  172. },
  173. { // try removing victim directory (symlink)
  174. {
  175. Name: "loophole-victim",
  176. Typeflag: tar.TypeSymlink,
  177. Linkname: "../victim",
  178. Mode: 0o755,
  179. },
  180. {
  181. Name: "loophole-victim",
  182. Typeflag: tar.TypeReg,
  183. Mode: 0o644,
  184. },
  185. },
  186. } {
  187. if err := testBreakout("applylayer", "docker-TestApplyLayerInvalidSymlink", headers); err != nil {
  188. t.Fatalf("i=%d. %v", i, err)
  189. }
  190. }
  191. }
  192. func TestApplyLayerWhiteouts(t *testing.T) {
  193. wd, err := os.MkdirTemp("", "graphdriver-test-whiteouts")
  194. if err != nil {
  195. return
  196. }
  197. defer os.RemoveAll(wd)
  198. base := []string{
  199. ".baz",
  200. "bar/",
  201. "bar/bax",
  202. "bar/bay/",
  203. "baz",
  204. "foo/",
  205. "foo/.abc",
  206. "foo/.bcd/",
  207. "foo/.bcd/a",
  208. "foo/cde/",
  209. "foo/cde/def",
  210. "foo/cde/efg",
  211. "foo/fgh",
  212. "foobar",
  213. }
  214. type tcase struct {
  215. change, expected []string
  216. }
  217. tcases := []tcase{
  218. {
  219. base,
  220. base,
  221. },
  222. {
  223. []string{
  224. ".bay",
  225. ".wh.baz",
  226. "foo/",
  227. "foo/.bce",
  228. "foo/.wh..wh..opq",
  229. "foo/cde/",
  230. "foo/cde/efg",
  231. },
  232. []string{
  233. ".bay",
  234. ".baz",
  235. "bar/",
  236. "bar/bax",
  237. "bar/bay/",
  238. "foo/",
  239. "foo/.bce",
  240. "foo/cde/",
  241. "foo/cde/efg",
  242. "foobar",
  243. },
  244. },
  245. {
  246. []string{
  247. ".bay",
  248. ".wh..baz",
  249. ".wh.foobar",
  250. "foo/",
  251. "foo/.abc",
  252. "foo/.wh.cde",
  253. "bar/",
  254. },
  255. []string{
  256. ".bay",
  257. "bar/",
  258. "bar/bax",
  259. "bar/bay/",
  260. "foo/",
  261. "foo/.abc",
  262. "foo/.bce",
  263. },
  264. },
  265. {
  266. []string{
  267. ".abc",
  268. ".wh..wh..opq",
  269. "foobar",
  270. },
  271. []string{
  272. ".abc",
  273. "foobar",
  274. },
  275. },
  276. }
  277. for i, tc := range tcases {
  278. l, err := makeTestLayer(tc.change)
  279. if err != nil {
  280. t.Fatal(err)
  281. }
  282. _, err = UnpackLayer(wd, l, nil)
  283. if err != nil {
  284. t.Fatal(err)
  285. }
  286. err = l.Close()
  287. if err != nil {
  288. t.Fatal(err)
  289. }
  290. paths, err := readDirContents(wd)
  291. if err != nil {
  292. t.Fatal(err)
  293. }
  294. if !reflect.DeepEqual(tc.expected, paths) {
  295. t.Fatalf("invalid files for layer %d: expected %q, got %q", i, tc.expected, paths)
  296. }
  297. }
  298. }
  299. func makeTestLayer(paths []string) (rc io.ReadCloser, err error) {
  300. tmpDir, err := os.MkdirTemp("", "graphdriver-test-mklayer")
  301. if err != nil {
  302. return
  303. }
  304. defer func() {
  305. if err != nil {
  306. os.RemoveAll(tmpDir)
  307. }
  308. }()
  309. for _, p := range paths {
  310. // Source files are always in Unix format. But we use filepath on
  311. // creation to be platform agnostic.
  312. if p[len(p)-1] == '/' {
  313. if err = os.MkdirAll(filepath.Join(tmpDir, p), 0o700); err != nil {
  314. return
  315. }
  316. } else {
  317. if err = os.WriteFile(filepath.Join(tmpDir, p), nil, 0o600); err != nil {
  318. return
  319. }
  320. }
  321. }
  322. archive, err := Tar(tmpDir, Uncompressed)
  323. if err != nil {
  324. return
  325. }
  326. return ioutils.NewReadCloserWrapper(archive, func() error {
  327. err := archive.Close()
  328. os.RemoveAll(tmpDir)
  329. return err
  330. }), nil
  331. }
  332. func readDirContents(root string) ([]string, error) {
  333. var files []string
  334. err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
  335. if err != nil {
  336. return err
  337. }
  338. if path == root {
  339. return nil
  340. }
  341. rel, err := filepath.Rel(root, path)
  342. if err != nil {
  343. return err
  344. }
  345. if info.IsDir() {
  346. rel = rel + string(filepath.Separator)
  347. }
  348. // Append in Unix semantics
  349. files = append(files, filepath.ToSlash(rel))
  350. return nil
  351. })
  352. if err != nil {
  353. return nil, err
  354. }
  355. return files, nil
  356. }