ww: enforce internal package visibility
This commit is contained in:
@@ -522,6 +522,7 @@ def SEP_ROLE_NORMAL: i32 = 0;
|
||||
def SEP_ROLE_TEST_SUPPORT: i32 = 1;
|
||||
def SEP_ROLE_GENERATED_MAIN: i32 = 2;
|
||||
def SEP_TEST_SUPPORT_MODULE: str = "__wwtest";
|
||||
def SEP_LOAD_INTERNAL: i32 = -3;
|
||||
def SEP_INITIAL_CAP: i32 = 8;
|
||||
def SEP_COUNT_MAX: i32 = 2147483647;
|
||||
|
||||
@@ -1345,6 +1346,115 @@ fn sepjoinpath(dir: *u8, name: *u8) *u8 = {
|
||||
return sepjoinpathlit(dir, pathstr(name));
|
||||
};
|
||||
|
||||
fn sepphysicaljoin(dir: *u8, name: str) *u8 = {
|
||||
if (cstrlen(dir) == 1u64 && dir[0u64] == '/': u8) {
|
||||
return sepappendlit(dir, name);
|
||||
};
|
||||
return sepjoinpathlit(dir, name);
|
||||
};
|
||||
|
||||
fn seppendingpath(target: str, rest: *u8, requiredir: bool) *u8 = {
|
||||
let restlen: u64 = cstrlen(rest);
|
||||
let need: u64 = 0u64;
|
||||
if (!sepaddbytes(&need, target.len: u64)
|
||||
|| (target.len != 0 && (restlen != 0 || requiredir)
|
||||
&& !sepaddbytes(&need, 1u64))
|
||||
|| !sepaddbytes(&need, restlen)
|
||||
|| !sepaddbytes(&need, 1u64)) { return nil; };
|
||||
let out: []u8;
|
||||
if (!sepmakebytes(need, &out)) { return nil; };
|
||||
let off: u64 = strinto(out.ptr, 0u64, target);
|
||||
if (target.len != 0 && (restlen != 0 || requiredir)) {
|
||||
off = byteinto(out.ptr, off, '/': u8);
|
||||
};
|
||||
off = cstrinto(out.ptr, off, rest);
|
||||
cstrseal(out.ptr, off);
|
||||
return out.ptr;
|
||||
};
|
||||
|
||||
fn sepphysicalparent(path: *u8) *u8 = {
|
||||
let n: u64 = cstrlen(path);
|
||||
for (n > 1u64 && path[n - 1u64] == '/': u8) { n -= 1u64; };
|
||||
for (n > 1u64 && path[n - 1u64] != '/': u8) { n -= 1u64; };
|
||||
if (n > 1u64) { n -= 1u64; };
|
||||
return sepdupcstr(path, n);
|
||||
};
|
||||
|
||||
fn sepcanonicalfile(path: *u8) *u8 = {
|
||||
if (path[0u64] == 0u8) { return nil; };
|
||||
let pending: *u8 = sepdupcstr(path, cstrlen(path));
|
||||
if (pending == nil) { return nil; };
|
||||
let resolved: *u8 = nil;
|
||||
if (path[0u64] == '/': u8) {
|
||||
resolved = sepdupcstr("/".ptr, 1u64);
|
||||
} else {
|
||||
let cwd: []u8;
|
||||
if (!sepmakebytes(os.PATH_MAX: u64, &cwd)) { return nil; };
|
||||
let n: i64 = os.getcwd(cwd.ptr, cwd.len: u64);
|
||||
if (n <= 1i64 || n > cwd.len: i64) { return nil; };
|
||||
resolved = sepdupcstr(cwd.ptr, (n - 1i64): u64);
|
||||
};
|
||||
if (resolved == nil) { return nil; };
|
||||
let links: i32 = 0;
|
||||
for (true) {
|
||||
let total: u64 = cstrlen(pending);
|
||||
let start: u64 = 0u64;
|
||||
for (start < total && pending[start] == '/': u8) { start += 1u64; };
|
||||
if (start == total) { return resolved; };
|
||||
let end: u64 = start;
|
||||
for (end < total && pending[end] != '/': u8) { end += 1u64; };
|
||||
let followed: bool = end < total;
|
||||
let rest: u64 = end;
|
||||
for (rest < total && pending[rest] == '/': u8) { rest += 1u64; };
|
||||
let requiredir: bool = followed && rest == total;
|
||||
let name: str;
|
||||
name.ptr = pending + start;
|
||||
name.len = (end - start): i32;
|
||||
if (name.len == 1 && name[0] == '.': u8) {
|
||||
pending = pending + rest;
|
||||
continue;
|
||||
};
|
||||
if (name.len == 2 && name[0] == '.': u8 && name[1] == '.': u8) {
|
||||
resolved = sepphysicalparent(resolved);
|
||||
if (resolved == nil) { return nil; };
|
||||
pending = pending + rest;
|
||||
continue;
|
||||
};
|
||||
let candidate: *u8 = sepphysicaljoin(resolved, name);
|
||||
if (candidate == nil) { return nil; };
|
||||
let fi: os.filestat;
|
||||
match (os.lstat(&fi, pathstr(candidate))) {
|
||||
case void => void;
|
||||
case let e: os.oserror => return nil;
|
||||
};
|
||||
let typ: u32 = (fi.mode: u32) & 61440u32;
|
||||
if (typ == os.mode.LINK: u32) {
|
||||
if (links == 40) { return nil; };
|
||||
links += 1;
|
||||
let target: []u8;
|
||||
if (!sepmakebytes(os.PATH_MAX: u64, &target)) { return nil; };
|
||||
let n: i64 = os.readlink(pathstr(candidate), target.ptr,
|
||||
os.PATH_MAX: u64);
|
||||
if (n < 0i64 || n >= os.PATH_MAX: i64) { return nil; };
|
||||
let ni: i32 = n: i32;
|
||||
target[ni] = 0u8;
|
||||
let targetname: str;
|
||||
targetname.ptr = target.ptr;
|
||||
targetname.len = ni;
|
||||
if (ni > 0 && target[0] == '/': u8) {
|
||||
resolved = sepdupcstr("/".ptr, 1u64);
|
||||
if (resolved == nil) { return nil; };
|
||||
};
|
||||
pending = seppendingpath(targetname, pending + rest, requiredir);
|
||||
if (pending == nil) { return nil; };
|
||||
continue;
|
||||
};
|
||||
if (followed && typ != os.mode.DIR: u32) { return nil; };
|
||||
resolved = candidate;
|
||||
pending = pending + rest;
|
||||
};
|
||||
};
|
||||
|
||||
fn sepdirectoryvariant(variant: i32) bool = {
|
||||
return variant == SEP_VARIANT_PRODUCTION
|
||||
|| variant == SEP_VARIANT_SAME_TEST
|
||||
@@ -1422,6 +1532,106 @@ fn sepforbiddencommandimport(g: *sepgraph, importer: i32, dep: i32) bool = {
|
||||
&& cstreq(from.canon, to.canon));
|
||||
};
|
||||
|
||||
fn sepinternalparentcount(path: *u8, parents: *u64) bool = {
|
||||
let total: u64 = cstrlen(path);
|
||||
let p: u64 = 0u64;
|
||||
let components: u64 = 0u64;
|
||||
let final: u64 = 0u64;
|
||||
let found: bool = false;
|
||||
for (p < total) {
|
||||
for (p < total && path[p] == '.': u8) { p += 1u64; };
|
||||
if (p == total) { break; };
|
||||
let end: u64 = p;
|
||||
for (end < total && path[end] != '.': u8) { end += 1u64; };
|
||||
if (end - p == "internal".len: u64
|
||||
&& bytecmp(path + p, end - p, "internal".ptr,
|
||||
"internal".len: u64) == 0) {
|
||||
final = components;
|
||||
found = true;
|
||||
};
|
||||
components += 1u64;
|
||||
p = end;
|
||||
};
|
||||
if (!found) { return false; };
|
||||
*parents = components - final;
|
||||
return true;
|
||||
};
|
||||
|
||||
fn seprawimporterdir(p: *seppkg) *u8 = {
|
||||
let total: u64 = cstrlen(p.canon);
|
||||
let slash: u64 = total;
|
||||
let i: u64 = 0u64;
|
||||
for (i < total) {
|
||||
if (p.canon[i] == '/': u8) { slash = i; };
|
||||
i += 1u64;
|
||||
};
|
||||
if (slash == total) { return nil; };
|
||||
if (slash == 0u64) { slash = 1u64; };
|
||||
return sepdupcstr(p.canon, slash);
|
||||
};
|
||||
|
||||
fn sepcanonicalinternalowner(path: *u8, parents: u64) *u8 = {
|
||||
let boundary: u64 = cstrlen(path);
|
||||
for (boundary > 1u64 && path[boundary - 1u64] == '/': u8) {
|
||||
boundary -= 1u64;
|
||||
};
|
||||
let pi: u64 = 0u64;
|
||||
for (pi < parents) {
|
||||
for (boundary > 0u64 && path[boundary - 1u64] != '/': u8) {
|
||||
boundary -= 1u64;
|
||||
};
|
||||
for (boundary > 1u64 && path[boundary - 1u64] == '/': u8) {
|
||||
boundary -= 1u64;
|
||||
};
|
||||
pi += 1u64;
|
||||
};
|
||||
let lexical: *u8 = nil;
|
||||
if (boundary == 0u64) {
|
||||
lexical = sepdupcstr(".".ptr, 1u64);
|
||||
} else {
|
||||
lexical = sepdupcstr(path, boundary);
|
||||
};
|
||||
if (lexical == nil) { return nil; };
|
||||
return canonicaldir(pathstr(lexical));
|
||||
};
|
||||
|
||||
fn sepinternalimportallowed(from: *seppkg, targetpath: *u8,
|
||||
targetentry: *u8) i32 = {
|
||||
let parents: u64 = 0u64;
|
||||
if (!sepinternalparentcount(targetpath, &parents)) { return 1; };
|
||||
let importer: *u8 = from.canon;
|
||||
if (from.isdir == 0) {
|
||||
importer = seprawimporterdir(from);
|
||||
if (importer == nil) {
|
||||
if (!sepfatalallocation) {
|
||||
cerr("ww: cannot canonicalize package ");
|
||||
cerr(pathstr(from.entry)); cerr("\n");
|
||||
};
|
||||
return -1;
|
||||
};
|
||||
};
|
||||
let owner: *u8 = sepcanonicalinternalowner(targetentry, parents);
|
||||
if (owner == nil) {
|
||||
if (!sepfatalallocation) {
|
||||
cerr("ww: cannot canonicalize package ");
|
||||
cerr(pathstr(targetentry)); cerr("\n");
|
||||
};
|
||||
return -1;
|
||||
};
|
||||
let boundary: u64 = cstrlen(owner);
|
||||
let n: u64 = cstrlen(importer);
|
||||
if (n == boundary
|
||||
&& bytecmp(importer, boundary, owner, boundary) == 0) {
|
||||
return 1;
|
||||
};
|
||||
if (boundary == 1u64 && owner[0u64] == '/': u8
|
||||
&& importer[0u64] == '/': u8) { return 1; };
|
||||
if (n > boundary
|
||||
&& bytecmp(importer, boundary, owner, boundary) == 0
|
||||
&& importer[boundary] == '/': u8) { return 1; };
|
||||
return 0;
|
||||
};
|
||||
|
||||
fn sepcommandcompilermarker(g: *sepgraph, pi: i32) bool = {
|
||||
return sepcommanddeclaredname(&g.pkg[pi]) && !g.pkg[pi].linkentry;
|
||||
};
|
||||
@@ -1494,12 +1704,14 @@ fn sepfindoraddvariant(g: *sepgraph, path: *u8, entry: *u8,
|
||||
let canon: *u8 = nil;
|
||||
if (isdir != 0) {
|
||||
canon = canonicaldir(pathstr(entry));
|
||||
if (canon == nil) {
|
||||
if (sepfatalallocation) { return -1; };
|
||||
cerr("ww: cannot canonicalize package ");
|
||||
cerr(pathstr(entry)); cerr("\n");
|
||||
return -1;
|
||||
};
|
||||
} else {
|
||||
canon = sepcanonicalfile(entry);
|
||||
};
|
||||
if (canon == nil) {
|
||||
if (sepfatalallocation) { return -1; };
|
||||
cerr("ww: cannot canonicalize package ");
|
||||
cerr(pathstr(entry)); cerr("\n");
|
||||
return -1;
|
||||
};
|
||||
let incoming: *u8 = nil;
|
||||
if (isdir != 0 && path[0u64] != 0u8) {
|
||||
@@ -2112,19 +2324,19 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, searchpath: *u8,
|
||||
};
|
||||
let externalproduction: bool = false;
|
||||
let ipath: *u8 = nil;
|
||||
let visibilityentry: *u8 = nil;
|
||||
if (g.pkg[pi].variant == SEP_VARIANT_EXTERNAL
|
||||
&& g.pkg[pi].importbase != nil
|
||||
&& cstrlen(g.pkg[pi].importbase) == idn
|
||||
&& bytecmp(g.pkg[pi].importbase, idn, idp, idn) == 0) {
|
||||
ipath = g.pkg[pi].canon;
|
||||
visibilityentry = g.pkg[pi].entry;
|
||||
};
|
||||
if (ipath == nil) {
|
||||
ipath = locateimport(searchpath, idp, idn);
|
||||
visibilityentry = ipath;
|
||||
};
|
||||
if (ipath != nil) {
|
||||
if (!sepbindadd(bindings, 'D': u8, u.usepath, ipath)) {
|
||||
return -1;
|
||||
};
|
||||
let self: bool = os.samefile(pathstr(ipath),
|
||||
pathstr(g.pkg[pi].entry));
|
||||
if (self && (sepexternalname(&g.pkg[pi], idp, idn, true)
|
||||
@@ -2150,6 +2362,18 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, searchpath: *u8,
|
||||
// action. Discovery role and product artifact never create another.
|
||||
let di: i32 = sepfindoradd(g, nm.ptr, ipath, 1);
|
||||
if (di < 0) { return -1; };
|
||||
let allowed: i32 = sepinternalimportallowed(&g.pkg[pi],
|
||||
g.pkg[di].path, visibilityentry);
|
||||
if (allowed < 0) { return -1; };
|
||||
if (allowed == 0) {
|
||||
cerrpos(u.file, u.line, u.col);
|
||||
cerr(": error: use of internal package ");
|
||||
cerr(pathstr(g.pkg[di].path)); cerr(" not allowed\n");
|
||||
return SEP_LOAD_INTERNAL;
|
||||
};
|
||||
if (!sepbindadd(bindings, 'D': u8, u.usepath, ipath)) {
|
||||
return -1;
|
||||
};
|
||||
if (!sepadddep(g, pi, di)) { return -1; };
|
||||
} else {
|
||||
let lstart: u64 = 0u64;
|
||||
@@ -2510,7 +2734,8 @@ fn seploadpkg(g: *sepgraph, pi: i32, context: i32) i32 = {
|
||||
nframe -= 1;
|
||||
continue;
|
||||
};
|
||||
if (seppreparepkgcontext(g, f.pkg, f.context) < 0) {
|
||||
let prepared: i32 = seppreparepkgcontext(g, f.pkg, f.context);
|
||||
if (prepared < 0) {
|
||||
let fi: i32 = 0;
|
||||
for (fi < nframe) {
|
||||
g.pkg[frames[fi].pkg].failed = true;
|
||||
@@ -2519,7 +2744,7 @@ fn seploadpkg(g: *sepgraph, pi: i32, context: i32) i32 = {
|
||||
if (sepfatalallocation) {
|
||||
return sepfinishloadframes(frames, -2);
|
||||
};
|
||||
return sepfinishloadframes(frames, -1);
|
||||
return sepfinishloadframes(frames, prepared);
|
||||
};
|
||||
f.nextdep = 0;
|
||||
};
|
||||
@@ -3771,6 +3996,7 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
let loadresult: i32 = seploadpkg(g, root,
|
||||
products[producti].context);
|
||||
if (loadresult == -2) { return 1; };
|
||||
if (loadresult == SEP_LOAD_INTERNAL) { return 1; };
|
||||
if (loadresult < 0) {
|
||||
g.pkg[root].failed = true;
|
||||
producti += 1;
|
||||
@@ -3793,6 +4019,7 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
let loadresult: i32 = seploadpkg(g, support,
|
||||
products[producti].context);
|
||||
if (loadresult == -2) { return 1; };
|
||||
if (loadresult == SEP_LOAD_INTERNAL) { return 1; };
|
||||
if (loadresult < 0) {
|
||||
g.pkg[variant].failed = true;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user