From 3fbdb1b349a27fd6720807713790e2332c5d5ccb Mon Sep 17 00:00:00 2001 From: Haik Aftandilian Date: Fri, 18 Aug 2017 16:12:07 -0700 Subject: [PATCH] Bug 1382260 - Patch 2 - [Mac] Allow reading of font files from the content sandbox. r=Alex_Gaynor MozReview-Commit-ID: 9W5aqQweFmd --HG-- extra : rebase_source : 9aa778bc08bee206e7f3340eac32ca2f46a4f81b --- security/sandbox/mac/SandboxPolicies.h | 13 +++ .../test/browser_content_sandbox_fs.js | 92 ++++++++++++++++++- .../test/browser_content_sandbox_utils.js | 6 ++ 3 files changed, 109 insertions(+), 2 deletions(-) diff --git a/security/sandbox/mac/SandboxPolicies.h b/security/sandbox/mac/SandboxPolicies.h index 24f18e00f015..59595c906351 100644 --- a/security/sandbox/mac/SandboxPolicies.h +++ b/security/sandbox/mac/SandboxPolicies.h @@ -342,6 +342,19 @@ static const char contentSandboxRules[] = R"( (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 )"; } diff --git a/security/sandbox/test/browser_content_sandbox_fs.js b/security/sandbox/test/browser_content_sandbox_fs.js index e38c47069099..9a8dc87d466c 100644 --- a/security/sandbox/test/browser_content_sandbox_fs.js +++ b/security/sandbox/test/browser_content_sandbox_fs.js @@ -9,6 +9,8 @@ var prefs = Cc["@mozilla.org/preferences-service;1"] 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 @@ -21,7 +23,7 @@ Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/" + 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) { @@ -29,7 +31,6 @@ function createFile(path) { }); } - // 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. @@ -246,6 +247,46 @@ async function createTempFile() { } } +// 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 @@ -276,6 +317,53 @@ async function testFileAccess() { // 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. diff --git a/security/sandbox/test/browser_content_sandbox_utils.js b/security/sandbox/test/browser_content_sandbox_utils.js index 820ec337fc94..332378219409 100644 --- a/security/sandbox/test/browser_content_sandbox_utils.js +++ b/security/sandbox/test/browser_content_sandbox_utils.js @@ -89,3 +89,9 @@ function GetDir(path) { 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); +}