--- a/security/sandbox/test/browser_content_sandbox_fs.js
+++ b/security/sandbox/test/browser_content_sandbox_fs.js
@@ -83,16 +83,29 @@ function readFile(path) {
let promise = OS.File.read(path).then(function (binaryData) {
return {ok: true};
}).catch(function (error) {
return {ok: false};
});
return promise;
}
+// Does a stat of |path| and returns a promise that resolves if the
+// stat is successful. Returned object has boolean .ok to indicate
+// success or failure.
+function statPath(path) {
+ Components.utils.import("resource://gre/modules/osfile.jsm");
+ let promise = OS.File.stat(path).then(function (stat) {
+ return {ok: true};
+ }).catch(function (error) {
+ return {ok: false};
+ });
+ return promise;
+}
+
// Returns true if the current content sandbox level, passed in
// the |level| argument, supports filesystem sandboxing.
function isContentFileIOSandboxed(level) {
let fileIOSandboxMinLevel = 0;
// Set fileIOSandboxMinLevel to the lowest level that has
// content filesystem sandboxing enabled. For now, this
// varies across Windows, Mac, Linux, other.
@@ -342,91 +355,99 @@ async function testFileAccess() {
let fontFile = GetFile(fontPath);
tests.push({
desc: "font file", // description
ok: true, // expected to succeed?
browser: webBrowser, // browser to run test in
file: fontFile, // nsIFile object
minLevel: minHomeReadSandboxLevel(), // min level to enable test
+ func: readFile, // the test function to use
});
}
for (let fontPath of badFontTestPaths) {
let result = await createFile(fontPath);
Assert.ok(result, `${fontPath} created`);
let fontFile = GetFile(fontPath);
tests.push({
desc: "invalid font file", // description
ok: false, // expected to succeed?
browser: webBrowser, // browser to run test in
file: fontFile, // nsIFile object
minLevel: minHomeReadSandboxLevel(), // min level to enable test
+ func: readFile, // the test function to use
});
}
}
// The Linux test runners create the temporary profile in the same
// system temp dir we give write access to, so this gives a false
// positive.
let profileDir = GetProfileDir();
if (!isLinux()) {
tests.push({
desc: "profile dir", // description
ok: false, // expected to succeed?
browser: webBrowser, // browser to run test in
file: profileDir, // nsIFile object
minLevel: minProfileReadSandboxLevel(), // min level to enable test
+ func: readDir,
});
}
if (fileContentProcessEnabled) {
tests.push({
desc: "profile dir",
ok: true,
browser: fileBrowser,
file: profileDir,
minLevel: 0,
+ func: readDir,
});
}
let homeDir = GetHomeDir();
tests.push({
desc: "home dir",
ok: false,
browser: webBrowser,
file: homeDir,
minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
});
if (fileContentProcessEnabled) {
tests.push({
desc: "home dir",
ok: true,
browser: fileBrowser,
file: homeDir,
minLevel: 0,
+ func: readDir,
});
}
let sysExtDevDir = GetSystemExtensionsDevDir();
tests.push({
desc: "system extensions dev dir",
ok: true,
browser: webBrowser,
file: sysExtDevDir,
minLevel: 0,
+ func: readDir,
});
if (isWin()) {
let extDir = GetPerUserExtensionDir();
tests.push({
desc: "per-user extensions dir",
ok: true,
browser: webBrowser,
file: extDir,
minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
});
}
if (isMac()) {
// If ~/Library/Caches/TemporaryItems exists, when level <= 2 we
// make sure it's readable. For level 3, we make sure it isn't.
let homeTempDir = GetHomeDir();
homeTempDir.appendRelativePath("Library/Caches/TemporaryItems");
@@ -440,16 +461,17 @@ async function testFileAccess() {
minLevel = 0;
}
tests.push({
desc: "home library cache temp dir",
ok: shouldBeReadable,
browser: webBrowser,
file: homeTempDir,
minLevel,
+ func: readDir,
});
}
}
if (isMac() || isLinux()) {
let varDir = GetDir("/var");
if (isMac()) {
@@ -460,24 +482,26 @@ async function testFileAccess() {
}
tests.push({
desc: "/var",
ok: false,
browser: webBrowser,
file: varDir,
minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
});
if (fileContentProcessEnabled) {
tests.push({
desc: "/var",
ok: true,
browser: fileBrowser,
file: varDir,
minLevel: 0,
+ func: readDir,
});
}
}
if (isMac()) {
// Test if we can read from $TMPDIR because we expect it
// to be within /private/var. Reading from it should be
// prevented in a 'web' process.
@@ -488,126 +512,202 @@ async function testFileAccess() {
"$TMPDIR is in /private/var");
tests.push({
desc: `$TMPDIR (${macTempDir.path})`,
ok: false,
browser: webBrowser,
file: macTempDir,
minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
});
if (fileContentProcessEnabled) {
tests.push({
desc: `$TMPDIR (${macTempDir.path})`,
ok: true,
browser: fileBrowser,
file: macTempDir,
minLevel: 0,
+ func: readDir,
});
}
// Test that we cannot read from /Volumes at level 3
let volumes = GetDir("/Volumes");
tests.push({
desc: "/Volumes",
ok: false,
browser: webBrowser,
file: volumes,
minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
});
// Test that we cannot read from /Network at level 3
let network = GetDir("/Network");
tests.push({
desc: "/Network",
ok: false,
browser: webBrowser,
file: network,
minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
});
// Test that we cannot read from /Users at level 3
let users = GetDir("/Users");
tests.push({
desc: "/Users",
ok: false,
browser: webBrowser,
file: users,
minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
+ });
+
+ // Test that we can stat /Users at level 3
+ tests.push({
+ desc: "/Users",
+ ok: true,
+ browser: webBrowser,
+ file: users,
+ minLevel: minHomeReadSandboxLevel(),
+ func: statPath,
+ });
+
+ // Test that we can stat /Library at level 3, but can't
+ // stat something within /Library. This test uses "/Library"
+ // because it's a path that is expected to always be present
+ // and isn't something content processes have read access to
+ // (just read-metadata).
+ let libraryDir = GetDir("/Library");
+ tests.push({
+ desc: "/Library",
+ ok: true,
+ browser: webBrowser,
+ file: libraryDir,
+ minLevel: minHomeReadSandboxLevel(),
+ func: statPath,
+ });
+ tests.push({
+ desc: "/Library",
+ ok: false,
+ browser: webBrowser,
+ file: libraryDir,
+ minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
+ });
+ let libraryWidgetsDir = GetDir("/Library/Widgets");
+ tests.push({
+ desc: "/Library/Widgets",
+ ok: false,
+ browser: webBrowser,
+ file: libraryWidgetsDir,
+ minLevel: minHomeReadSandboxLevel(),
+ func: statPath,
+ });
+
+ // Similarly, test that we can stat /private, but not /private/etc.
+ let privateDir = GetDir("/private");
+ tests.push({
+ desc: "/private",
+ ok: true,
+ browser: webBrowser,
+ file: privateDir,
+ minLevel: minHomeReadSandboxLevel(),
+ func: statPath,
+ });
+ let privateEtcDir = GetFile("/private/etc");
+ tests.push({
+ desc: "/private/etc",
+ ok: false,
+ browser: webBrowser,
+ file: privateEtcDir,
+ minLevel: minHomeReadSandboxLevel(),
+ func: statPath,
});
}
let extensionsDir = GetProfileEntry("extensions");
if (extensionsDir.exists() && extensionsDir.isDirectory()) {
tests.push({
desc: "extensions dir",
ok: true,
browser: webBrowser,
file: extensionsDir,
minLevel: 0,
+ func: readDir,
});
} else {
ok(false, `${extensionsDir.path} is a valid dir`);
}
let chromeDir = GetProfileEntry("chrome");
if (chromeDir.exists() && chromeDir.isDirectory()) {
tests.push({
desc: "chrome dir",
ok: true,
browser: webBrowser,
file: chromeDir,
minLevel: 0,
+ func: readDir,
});
} else {
ok(false, `${chromeDir.path} is valid dir`);
}
let cookiesFile = GetProfileEntry("cookies.sqlite");
if (cookiesFile.exists() && !cookiesFile.isDirectory()) {
// On Linux, the temporary profile used for tests is in the system
// temp dir which content has read access to, so this test fails.
if (!isLinux()) {
tests.push({
desc: "cookies file",
ok: false,
browser: webBrowser,
file: cookiesFile,
minLevel: minProfileReadSandboxLevel(),
+ func: readFile,
});
}
if (fileContentProcessEnabled) {
tests.push({
desc: "cookies file",
ok: true,
browser: fileBrowser,
file: cookiesFile,
minLevel: 0,
+ func: readFile,
});
}
} else {
ok(false, `${cookiesFile.path} is a valid file`);
}
// remove tests not enabled by the current sandbox level
tests = tests.filter((test) => (test.minLevel <= level));
for (let test of tests) {
- let testFunc = test.file.isDirectory() ? readDir : readFile;
let okString = test.ok ? "allowed" : "blocked";
let processType = test.browser === webBrowser ? "web" : "file";
+ // ensure the file/dir exists before we ask a content process to stat
+ // it so we know a failure is not due to a nonexistent file/dir
+ if (test.func === statPath) {
+ ok(test.file.exists(), `${test.file.path} exists`);
+ }
+
let result = await ContentTask.spawn(test.browser, test.file.path,
- testFunc);
+ test.func);
ok(result.ok == test.ok,
`reading ${test.desc} from a ${processType} process ` +
`is ${okString} (${test.file.path})`);
// if the directory is not expected to be readable,
// ensure the listing has zero entries
- if (test.file.isDirectory() && !test.ok) {
+ if (test.func === readDir && !test.ok) {
ok(result.numEntries == 0, `directory list is empty (${test.file.path})`);
}
}
if (fileContentProcessEnabled) {
gBrowser.removeTab(gBrowser.selectedTab);
}
}