ww: enforce internal package visibility
This commit is contained in:
118
cmd/ww/main.c
118
cmd/ww/main.c
@@ -373,6 +373,7 @@ source_has_test_decl(const char *path)
|
||||
#define SEP_ROLE_TEST_SUPPORT 1
|
||||
#define SEP_ROLE_GENERATED_MAIN 2
|
||||
#define SEP_TEST_SUPPORT_MODULE "__wwtest"
|
||||
#define SEP_LOAD_INTERNAL -3
|
||||
|
||||
/* Package-graph storage grows geometrically. Counts remain signed ints
|
||||
* because they are stable action/context indices throughout the existing
|
||||
@@ -994,6 +995,84 @@ sep_forbidden_command_import(const struct sepgraph *g, int importer, int dep)
|
||||
&& strcmp(from->canon, to->canon) == 0);
|
||||
}
|
||||
|
||||
static int
|
||||
sep_internal_parent_count(const char *path, size_t *parents)
|
||||
{
|
||||
const char *p = path;
|
||||
size_t components = 0;
|
||||
size_t final = 0;
|
||||
int found = 0;
|
||||
while (*p != '\0') {
|
||||
while (*p == '.') p++;
|
||||
if (*p == '\0') break;
|
||||
const char *end = strchr(p, '.');
|
||||
size_t n = end != NULL ? (size_t)(end - p) : strlen(p);
|
||||
if (n == sizeof "internal" - 1
|
||||
&& memcmp(p, "internal", n) == 0) {
|
||||
final = components;
|
||||
found = 1;
|
||||
}
|
||||
components++;
|
||||
if (end == NULL) break;
|
||||
p = end + 1;
|
||||
}
|
||||
if (!found) return 0;
|
||||
*parents = components - final;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
sep_internal_import_allowed(const struct seppkg *from,
|
||||
const char *target_path, const char *target_entry)
|
||||
{
|
||||
size_t parents;
|
||||
if (!sep_internal_parent_count(target_path, &parents)) return 1;
|
||||
const char *importer = from->canon;
|
||||
char *owned = NULL;
|
||||
if (!from->is_dir) {
|
||||
const char *slash = strrchr(from->canon, '/');
|
||||
if (slash == NULL) return -1;
|
||||
size_t n = slash == from->canon ? 1 : (size_t)(slash - from->canon);
|
||||
owned = strndup(from->canon, n);
|
||||
if (owned == NULL) return sep_fail_nomem();
|
||||
importer = owned;
|
||||
}
|
||||
size_t boundary = strlen(target_entry);
|
||||
while (boundary > 1 && target_entry[boundary - 1] == '/') boundary--;
|
||||
for (size_t i = 0; i < parents; i++) {
|
||||
while (boundary > 0 && target_entry[boundary - 1] != '/') boundary--;
|
||||
while (boundary > 1 && target_entry[boundary - 1] == '/') boundary--;
|
||||
}
|
||||
char *lexical = boundary == 0
|
||||
? strdup(".") : strndup(target_entry, boundary);
|
||||
if (lexical == NULL) {
|
||||
free(owned);
|
||||
return sep_fail_nomem();
|
||||
}
|
||||
errno = 0;
|
||||
char *owner = realpath(lexical, NULL);
|
||||
free(lexical);
|
||||
if (owner == NULL) {
|
||||
if (errno == ENOMEM)
|
||||
sep_fail_nomem();
|
||||
else
|
||||
fprintf(stderr, "ww: cannot canonicalize package %s\n",
|
||||
target_entry);
|
||||
free(owned);
|
||||
return -1;
|
||||
}
|
||||
size_t n = strlen(importer);
|
||||
boundary = strlen(owner);
|
||||
int allowed = (n == boundary
|
||||
&& memcmp(importer, owner, boundary) == 0)
|
||||
|| (boundary == 1 && owner[0] == '/' && importer[0] == '/')
|
||||
|| (n > boundary && memcmp(importer, owner, boundary) == 0
|
||||
&& importer[boundary] == '/');
|
||||
free(owner);
|
||||
free(owned);
|
||||
return allowed;
|
||||
}
|
||||
|
||||
static int
|
||||
sep_command_compiler_marker(const struct sepgraph *g, int pi)
|
||||
{
|
||||
@@ -1718,6 +1797,8 @@ sep_scan_file(struct sepgraph *g, int pi, const char *file,
|
||||
else
|
||||
located = locate_import(searchpath, path_form, ipath,
|
||||
sizeof ipath);
|
||||
const char *visibility_entry = bound != NULL
|
||||
? g->pkg[pi].entry : ipath;
|
||||
if (!located) {
|
||||
const char *dot = strrchr(name, '.');
|
||||
const char *leaf = dot ? dot + 1 : name;
|
||||
@@ -1751,13 +1832,7 @@ sep_scan_file(struct sepgraph *g, int pi, const char *file,
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
if (sep_binding_add(bindings, 'D', name, canon) < 0) {
|
||||
free(canon);
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
int self = strcmp(canon, g->pkg[pi].canon) == 0;
|
||||
free(canon);
|
||||
if (self
|
||||
&& (sep_external_production_name(&g->pkg[pi], name, 1)
|
||||
|| (g->pkg[pi].variant == SEP_VARIANT_EXTERNAL
|
||||
@@ -1768,6 +1843,7 @@ sep_scan_file(struct sepgraph *g, int pi, const char *file,
|
||||
? g->pkg[pi].path : g->pkg[pi].canon;
|
||||
errorf(u->pos, "self-import: package '%s' cannot import itself",
|
||||
owner[0] ? owner : "(root)");
|
||||
free(canon);
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
@@ -1775,7 +1851,23 @@ sep_scan_file(struct sepgraph *g, int pi, const char *file,
|
||||
* canonical production action for this directory. Discovery role and
|
||||
* the external product's artifact name never create another action. */
|
||||
int di = sep_find_or_add(g, name, ipath, 1);
|
||||
if (di < 0) { rc = -1; break; }
|
||||
if (di < 0) { free(canon); rc = -1; break; }
|
||||
int allowed = sep_internal_import_allowed(&g->pkg[pi],
|
||||
g->pkg[di].path, visibility_entry);
|
||||
if (allowed < 0) { free(canon); rc = -1; break; }
|
||||
if (!allowed) {
|
||||
errorf(u->pos, "use of internal package %s not allowed",
|
||||
g->pkg[di].path);
|
||||
free(canon);
|
||||
rc = SEP_LOAD_INTERNAL;
|
||||
break;
|
||||
}
|
||||
if (sep_binding_add(bindings, 'D', name, canon) < 0) {
|
||||
free(canon);
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
free(canon);
|
||||
if (sep_add_dep(g, pi, di) < 0) { rc = -1; break; }
|
||||
}
|
||||
}
|
||||
@@ -1989,7 +2081,7 @@ sep_prepare_pkg_context(struct sepgraph *g, int pi, int context)
|
||||
if (rc < 0) {
|
||||
g->pkg[pi].context_state[context] = 2;
|
||||
g->pkg[pi].failed = 1;
|
||||
return -1;
|
||||
return rc;
|
||||
}
|
||||
for (int i = 1; i < g->pkg[pi].ndeps; i++) {
|
||||
int v = g->pkg[pi].deps[i];
|
||||
@@ -2026,6 +2118,7 @@ sep_load_pkg(struct sepgraph *g, int pi, int context)
|
||||
context = g->support_context;
|
||||
struct seploadframe *frames = NULL;
|
||||
int nframe = 0, framecap = 0;
|
||||
int result = -1;
|
||||
if (sep_reserve((void **)&frames, &framecap, 1,
|
||||
sizeof *frames) < 0)
|
||||
return -2;
|
||||
@@ -2045,8 +2138,11 @@ sep_load_pkg(struct sepgraph *g, int pi, int context)
|
||||
nframe--;
|
||||
continue;
|
||||
}
|
||||
if (sep_prepare_pkg_context(g, f->pkg, f->context) < 0)
|
||||
int prepared = sep_prepare_pkg_context(g, f->pkg, f->context);
|
||||
if (prepared < 0) {
|
||||
result = prepared;
|
||||
goto failed;
|
||||
}
|
||||
f->next_dep = 0;
|
||||
}
|
||||
if (f->pending_dep >= 0) {
|
||||
@@ -2094,7 +2190,7 @@ failed:
|
||||
for (int i = 0; i < nframe; i++)
|
||||
g->pkg[frames[i].pkg].failed = 1;
|
||||
free(frames);
|
||||
return sep_fatal_allocation ? -2 : -1;
|
||||
return sep_fatal_allocation ? -2 : result;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -3016,6 +3112,7 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
||||
}
|
||||
int lr = sep_load_pkg(g, root, products[i].context);
|
||||
if (lr == -2) return 1;
|
||||
if (lr == SEP_LOAD_INTERNAL) return 1;
|
||||
if (lr < 0) {
|
||||
g->pkg[root].failed = 1;
|
||||
continue;
|
||||
@@ -3034,6 +3131,7 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
||||
if (support >= 0 && support != variant) {
|
||||
int lr = sep_load_pkg(g, support, products[i].context);
|
||||
if (lr == -2) return 1;
|
||||
if (lr == SEP_LOAD_INTERNAL) return 1;
|
||||
if (lr < 0) g->pkg[variant].failed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user