Files
flatpak/tests/test-ref-utils.c
T
Simon McVittie 12b79e1b11 tests: Add some unit tests for ref-utils name component validation
In the fix for GHSA-v2gw-v9h5-9q4x these functions are used to avoid
path traversal, so we'd better assert that they do detect and avoid it.

Signed-off-by: Simon McVittie <smcv@collabora.com>
Helps: https://github.com/flatpak/flatpak/security/advisories/GHSA-v2gw-v9h5-9q4x
2026-08-10 23:06:17 +02:00

361 lines
9.6 KiB
C

/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2020-2026 Collabora Ltd.
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <glib.h>
#include "flatpak.h"
#include "flatpak-ref-utils-private.h"
#include "tests/testlib.h"
static void
test_valid_arch (void)
{
static const char * const good[] =
{
"i386",
"mips64el",
"x86_64",
};
static const char * const bad[] =
{
"",
"--overrule-some-option",
".",
"..",
"a/b",
"x86-64",
"../path_traversal",
"path/../traversal",
"path_traversal/..",
"\xc3\xa1", /* U+00E1 LATIN SMALL LETTER A WITH ACUTE */
"a\xc3\xa1",
};
static const struct
{
const char *name;
int len;
}
initially_good[] =
{
{ "abc\xc3\xa1", 3 },
{ "_/", 1 },
};
for (size_t i = 0; i < G_N_ELEMENTS (good); i++)
{
g_autoptr(GError) local_error = NULL;
gboolean ok = flatpak_is_valid_arch (good[i], -1, &local_error);
g_test_message ("good architecture \"%s\" -> %s",
good[i], ok ? "OK" : local_error->message);
g_assert_no_error (local_error);
g_assert_true (ok);
}
for (size_t i = 0; i < G_N_ELEMENTS (initially_good); i++)
{
g_autoptr(GError) local_error = NULL;
gboolean ok = flatpak_is_valid_arch (initially_good[i].name,
initially_good[i].len,
&local_error);
g_test_message ("good architecture \"%.*s\" -> %s",
initially_good[i].len,
initially_good[i].name,
ok ? "OK" : local_error->message);
g_assert_no_error (local_error);
g_assert_true (ok);
}
for (size_t i = 0; i < G_N_ELEMENTS (bad); i++)
{
g_autoptr(GError) local_error = NULL;
gboolean ok = flatpak_is_valid_arch (bad[i], -1, &local_error);
g_test_message ("bad architecture \"%s\" -> %s",
bad[i], ok ? "wrongly accepted" : local_error->message);
g_assert_nonnull (local_error);
g_assert_false (ok);
}
{
g_autoptr(GError) local_error = NULL;
gboolean ok = flatpak_is_valid_arch (good[0], 0, &local_error);
g_test_message ("empty architecture -> %s",
ok ? "wrongly accepted" : local_error->message);
g_assert_nonnull (local_error);
g_assert_false (ok);
}
}
static void
test_valid_branch (void)
{
static const char * const good[] =
{
"stable",
"private-beta",
"public_beta",
"1.0",
"--whatever--",
"x",
};
static const char * const bad[] =
{
"",
".",
"..",
"a/b",
"../path-traversal",
"path-traversal/..",
"path/../traversal",
"\xc3\xa1", /* U+00E1 LATIN SMALL LETTER A WITH ACUTE */
"a\xc3\xa1",
};
static const struct
{
const char *name;
int len;
}
initially_good[] =
{
{ "abc\xc3\xa1", 3 },
{ "x/", 1 },
};
for (size_t i = 0; i < G_N_ELEMENTS (good); i++)
{
g_autoptr(GError) local_error = NULL;
gboolean ok = flatpak_is_valid_branch (good[i], -1, &local_error);
g_test_message ("good branch \"%s\" -> %s",
good[i], ok ? "OK" : local_error->message);
g_assert_no_error (local_error);
g_assert_true (ok);
}
for (size_t i = 0; i < G_N_ELEMENTS (initially_good); i++)
{
g_autoptr(GError) local_error = NULL;
gboolean ok = flatpak_is_valid_branch (initially_good[i].name,
initially_good[i].len,
&local_error);
g_test_message ("good branch \"%.*s\" -> %s",
initially_good[i].len,
initially_good[i].name,
ok ? "OK" : local_error->message);
g_assert_no_error (local_error);
g_assert_true (ok);
}
for (size_t i = 0; i < G_N_ELEMENTS (bad); i++)
{
g_autoptr(GError) local_error = NULL;
gboolean ok = flatpak_is_valid_branch (bad[i], -1, &local_error);
g_test_message ("bad branch \"%s\" -> %s",
bad[i], ok ? "wrongly accepted" : local_error->message);
g_assert_nonnull (local_error);
g_assert_false (ok);
}
{
g_autoptr(GError) local_error = NULL;
gboolean ok = flatpak_is_valid_branch (good[0], 0, &local_error);
g_test_message ("empty branch -> %s",
ok ? "wrongly accepted" : local_error->message);
g_assert_nonnull (local_error);
g_assert_false (ok);
}
}
#define NAME_CHAR_x16 "z12345678.abcdef"
#define NAME_CHAR_x32 NAME_CHAR_x16 NAME_CHAR_x16
#define NAME_CHAR_x64 NAME_CHAR_x32 NAME_CHAR_x32
#define NAME_CHAR_x128 NAME_CHAR_x64 NAME_CHAR_x64
#define NAME_CHAR_x256 NAME_CHAR_x128 NAME_CHAR_x128
static void
test_valid_name (void)
{
static const char * const good[] =
{
"com.example.Foo",
"a.b.c",
"org._7_zip.Archiver",
"org._7_zip._7-zip", /* "-" in last element discouraged but allowed */
"uk.co.pseudorandom.name.has.many.components",
};
static const char * const bad[] =
{
"",
"a.b", /* not enough elements */
"org.7_zip.Archiver",
"org._7-zip.Archiver",
"\xc3\xa1", /* U+00E1 LATIN SMALL LETTER A WITH ACUTE */
"a\xc3\xa1",
NAME_CHAR_x256,
};
static const struct
{
const char *name;
int len;
}
initially_good[] =
{
{ "a.b.c\xc3\xa1", 5 },
{ NAME_CHAR_x256, 255 },
};
for (size_t i = 0; i < G_N_ELEMENTS (good); i++)
{
g_autoptr(GError) local_error = NULL;
gboolean ok = flatpak_is_valid_name (good[i], -1, &local_error);
g_test_message ("good app name \"%s\" -> %s",
good[i], ok ? "OK" : local_error->message);
g_assert_no_error (local_error);
g_assert_true (ok);
}
for (size_t i = 0; i < G_N_ELEMENTS (initially_good); i++)
{
g_autoptr(GError) local_error = NULL;
gboolean ok = flatpak_is_valid_name (initially_good[i].name,
initially_good[i].len,
&local_error);
g_test_message ("good app name \"%.*s\" -> %s",
initially_good[i].len,
initially_good[i].name,
ok ? "OK" : local_error->message);
g_assert_no_error (local_error);
g_assert_true (ok);
}
for (size_t i = 0; i < G_N_ELEMENTS (bad); i++)
{
g_autoptr(GError) local_error = NULL;
gboolean ok = flatpak_is_valid_name (bad[i], -1, &local_error);
g_test_message ("bad app name \"%s\" -> %s",
bad[i], ok ? "wrongly accepted" : local_error->message);
g_assert_nonnull (local_error);
g_assert_false (ok);
}
{
g_autoptr(GError) local_error = NULL;
gboolean ok = flatpak_is_valid_name (good[0], 0, &local_error);
g_test_message ("empty app name -> %s",
ok ? "wrongly accepted" : local_error->message);
g_assert_nonnull (local_error);
g_assert_false (ok);
}
}
static void
test_valid_remote_name (void)
{
static const char * const good[] =
{
"7zip",
"_abc123",
"a",
"a_b",
"remote-1.0",
};
static const char * const bad[] =
{
"",
"--overrule-some-option",
".",
"..",
"a/b",
"../path-traversal",
"path/../traversal",
"path-traversal/..",
"\xc3\xa1", /* U+00E1 LATIN SMALL LETTER A WITH ACUTE */
"a\xc3\xa1",
};
static const struct
{
const char *name;
int len;
}
initially_good[] =
{
{ "abc\xc3\xa1", 3 },
{ "x/", 1 },
};
for (size_t i = 0; i < G_N_ELEMENTS (good); i++)
{
g_autoptr(GError) local_error = NULL;
gboolean ok = flatpak_is_valid_remote_name (good[i], -1, &local_error);
g_test_message ("good remote name \"%s\" -> %s",
good[i], ok ? "OK" : local_error->message);
g_assert_no_error (local_error);
g_assert_true (ok);
}
for (size_t i = 0; i < G_N_ELEMENTS (initially_good); i++)
{
g_autoptr(GError) local_error = NULL;
gboolean ok = flatpak_is_valid_remote_name (initially_good[i].name,
initially_good[i].len,
&local_error);
g_test_message ("good remote name \"%.*s\" -> %s",
initially_good[i].len,
initially_good[i].name,
ok ? "OK" : local_error->message);
g_assert_no_error (local_error);
g_assert_true (ok);
}
for (size_t i = 0; i < G_N_ELEMENTS (bad); i++)
{
g_autoptr(GError) local_error = NULL;
gboolean ok = flatpak_is_valid_remote_name (bad[i], -1, &local_error);
g_test_message ("bad remote name \"%s\" -> %s",
bad[i], ok ? "wrongly accepted" : local_error->message);
g_assert_nonnull (local_error);
g_assert_false (ok);
}
{
g_autoptr(GError) local_error = NULL;
gboolean ok = flatpak_is_valid_remote_name (good[0], 0, &local_error);
g_test_message ("empty remote name -> %s",
ok ? "wrongly accepted" : local_error->message);
g_assert_nonnull (local_error);
g_assert_false (ok);
}
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/ref-utils/valid-arch", test_valid_arch);
g_test_add_func ("/ref-utils/valid-branch", test_valid_branch);
g_test_add_func ("/ref-utils/valid-name", test_valid_name);
g_test_add_func ("/ref-utils/valid-remote-name", test_valid_remote_name);
return g_test_run ();
}