Bug 1382260 - Patch 2 - [Mac] Allow reading of font files from the content sandbox. r=Alex_Gaynor
MozReview-Commit-ID: 9W5aqQweFmd
--- a/security/sandbox/mac/SandboxPolicies.h
+++ b/security/sandbox/mac/SandboxPolicies.h
@@ -337,13 +337,26 @@ static const char contentSandboxRules[]
(allow file-read* file-write-data
(subpath appTempDir))
(allow file-write-create
(require-all
(subpath appTempDir)
(require-any
(vnode-type REGULAR-FILE)
(vnode-type DIRECTORY))))
+
+ ; bug 1382260
+ ; We may need to load fonts from outside of the standard
+ ; font directories whitelisted above. This is typically caused
+ ; by a font manager. For now, whitelist any file with a
+ ; font extension. Limit this to the common font types:
+ ; files ending in .otf, .ttf, .ttc, .otc, and .dfont.
+ (allow file-read*
+ (regex #"\.[oO][tT][fF]$" ; otf
+ #"\.[tT][tT][fF]$" ; ttf
+ #"\.[tT][tT][cC]$" ; ttc
+ #"\.[oO][tT][cC]$" ; otc
+ #"\.[dD][fF][oO][nN][tT]$")) ; dfont
)";
}
#endif // mozilla_SandboxPolicies_h
--- a/security/sandbox/test/browser_content_sandbox_fs.js
+++ b/security/sandbox/test/browser_content_sandbox_fs.js
@@ -4,37 +4,38 @@
"use strict";
var prefs = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefBranch);
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/" +
"security/sandbox/test/browser_content_sandbox_utils.js", this);
+const FONT_EXTENSIONS = [ "otf", "ttf", "ttc", "otc", "dfont" ];
+
/*
* This test exercises file I/O from web and file content processes using
* OS.File methods to validate that calls that are meant to be blocked by
* content sandboxing are blocked.
*/
// Creates file at |path| and returns a promise that resolves with true
// if the file was successfully created, otherwise false. Include imports
// so this can be safely serialized and run remotely by ContentTask.spawn.
function createFile(path) {
Components.utils.import("resource://gre/modules/osfile.jsm");
let encoder = new TextEncoder();
- let array = encoder.encode("WRITING FROM CONTENT PROCESS");
+ let array = encoder.encode("TEST FILE DUMMY DATA");
return OS.File.writeAtomic(path, array).then(function(value) {
return true;
}, function(reason) {
return false;
});
}
-
// Creates a symlink at |path| and returns a promise that resolves with true
// if the symlink was successfully created, otherwise false. Include imports
// so this can be safely serialized and run remotely by ContentTask.spawn.
function createSymlink(path) {
Components.utils.import("resource://gre/modules/osfile.jsm");
// source location for the symlink can be anything
return OS.File.unixSymLink("/Users", path).then(function(value) {
return true;
@@ -241,16 +242,56 @@ async function createTempFile() {
let symlinkCreated = await ContentTask.spawn(browser, path, createSymlink);
ok(symlinkCreated == false,
"created a symlink in content temp is not permitted");
} else {
ok(fileDeleted == true, "deleting a file in content temp is permitted");
}
}
+// Build a list of nonexistent font file paths (lower and upper case) with
+// all the font extensions we want the sandbox to allow read access to.
+// Generate paths within base directory |baseDir|.
+function getFontTestPaths(baseDir) {
+ baseDir = baseDir + "/";
+
+ let basename = uuid();
+ let testPaths = [];
+
+ for (let ext of FONT_EXTENSIONS) {
+ // lower case filename
+ let lcFilename = baseDir + (basename + "lc." + ext).toLowerCase();
+ testPaths.push(lcFilename);
+ // upper case filename
+ let ucFilename = baseDir + (basename + "UC." + ext).toUpperCase();
+ testPaths.push(ucFilename);
+ }
+ return testPaths;
+}
+
+// Build a list of nonexistent invalid font file paths. Specifically,
+// paths that include the valid font extensions but should fail to load.
+// For example, if a font extension happens to be a substring of the filename
+// but isn't the extension. Generate paths within base directory |baseDir|.
+function getBadFontTestPaths(baseDir) {
+ baseDir = baseDir + "/";
+
+ let basename = uuid();
+ let testPaths = [];
+
+ for (let ext of FONT_EXTENSIONS) {
+ let filename = baseDir + basename + "." + ext + ".txt";
+ testPaths.push(filename);
+
+ filename = baseDir + basename + "." + ext + ext + ".txt";
+ testPaths.push(filename);
+ }
+ return testPaths;
+}
+
// Test reading files and dirs from web and file content processes.
async function testFileAccess() {
// for tests that run in a web content process
let webBrowser = gBrowser.selectedBrowser;
// Ensure that the file content process is enabled.
let fileContentProcessEnabled =
prefs.getBoolPref("browser.tabs.remote.separateFileUriProcess");
@@ -271,16 +312,63 @@ async function testFileAccess() {
// Directories/files to test accessing from content processes.
// For directories, we test whether a directory listing is allowed
// or blocked. For files, we test if we can read from the file.
// Each entry in the array represents a test file or directory
// that will be read from either a web or file process.
let tests = [];
+ // Test that Mac content processes can read files with font extensions
+ // and fail to read files that include the font extension as a
+ // non-extension substring.
+ if (isMac()) {
+ // Use the same directory for valid/invalid font path tests to ensure
+ // the font isn't allowed because the directory is already allowed.
+ let fontTestDir = "/private/tmp";
+ let fontTestPaths = getFontTestPaths(fontTestDir);
+ let badFontTestPaths = getBadFontTestPaths(fontTestDir);
+
+ // before we start creating dummy font files,
+ // register a cleanup func to remove them
+ registerCleanupFunction(async function() {
+ for (let fontPath of fontTestPaths.concat(badFontTestPaths)) {
+ await OS.File.remove(fontPath, {ignoreAbsent: true});
+ }
+ });
+
+ // create each dummy font file and add a test for it
+ for (let fontPath of fontTestPaths) {
+ let result = await createFile(fontPath);
+ Assert.ok(result, `${fontPath} created`);
+
+ 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
+ });
+ }
+ 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
+ });
+ }
+ }
+
// 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?
--- a/security/sandbox/test/browser_content_sandbox_utils.js
+++ b/security/sandbox/test/browser_content_sandbox_utils.js
@@ -84,8 +84,14 @@ function GetDir(path) {
dir.initWithPath(path);
Assert.ok(dir.isDirectory(), `${path} is a directory`);
return (dir);
}
function GetDirFromEnvVariable(varName) {
return GetDir(environment.get(varName));
}
+
+function GetFile(path) {
+ let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ file.initWithPath(path);
+ return (file);
+}