Skip to content

fix(@angular/cli): add a flag to let assets outside of outDir #8123

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion docs/documentation/stories/asset-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,27 @@ The array below does the same as the default one:
]
```

The contents of `node_modules/some-package/images/` will be available in `dist/some-package/`.
The contents of `node_modules/some-package/images/` will be available in `dist/some-package/`.

## Writing assets outside of `dist/`

Because of the security implications, the CLI will always refuse to read or write files outside of
the project itself (scoped by `.angular-cli.json`). It is however possible to write assets outside
the `dist/` build output folder during build.

Because writing files in your project isn't an expected effect of `ng build`, it is disabled by
default on every assets. In order to allow this behaviour, you need to set `allowOutsideOutDir`
to `true` on your asset definition, like so:

```json
"assets": [
{
"glob": "**/*",
"input": "./assets/",
"output": "../not-dist/some/folder/",
"allowOutsideOutDir": true
},
]
```

This needs to be set for every assets you want to write outside of your build output directory.
5 changes: 5 additions & 0 deletions packages/@angular/cli/lib/config/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@
"type": "string",
"default": "",
"description": "The output path (relative to the outDir)."
},
"allowOutsideOutDir": {
"type": "boolean",
"description": "Allow assets to be copied outside the outDir.",
"default": false
}
},
"additionalProperties": false
Expand Down
20 changes: 18 additions & 2 deletions packages/@angular/cli/models/webpack-configs/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,26 @@ export function getCommonConfig(wco: WebpackConfigOptions) {
asset.output = asset.output || '';
asset.glob = asset.glob || '';

// Prevent asset configurations from writing outside of the output path
// Prevent asset configurations from writing outside of the output path, except if the user
// specify a configuration flag.
// Also prevent writing outside the project path. That is not overridable.
const fullOutputPath = path.resolve(buildOptions.outputPath, asset.output);
if (!fullOutputPath.startsWith(projectRoot)) {
const message = 'An asset cannot be written to a location outside the project.';
throw new SilentError(message);
}
if (!fullOutputPath.startsWith(path.resolve(buildOptions.outputPath))) {
const message = 'An asset cannot be written to a location outside of the output path.';
if (!asset.allowOutsideOutDir) {
const message = 'An asset cannot be written to a location outside of the output path. '
+ 'You can override this message by setting the `allowOutsideOutDir` '
+ 'property on the asset to true in the CLI configuration.';
throw new SilentError(message);
}
}

// Prevent asset configurations from reading files outside of the project.
if (!asset.input.startsWith(projectRoot)) {
const message = 'An asset cannot be read from a location outside the project.';
throw new SilentError(message);
}

Expand Down
1 change: 1 addition & 0 deletions packages/@angular/cli/models/webpack-configs/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,5 @@ export interface AssetPattern {
glob: string;
input?: string;
output?: string;
allowOutsideOutDir?: boolean;
}
32 changes: 31 additions & 1 deletion tests/e2e/tests/build/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,40 @@ export default function () {
.then(() => updateJsonFile('.angular-cli.json', configJson => {
const app = configJson['apps'][0];
app['assets'] = [
{ 'glob': '**/*', 'input': '../node_modules/some-package/', 'output': '../package-folder' }
{ 'glob': '**/*', 'input': '../node_modules/some-package/', 'output': '../temp' }
];
}))
.then(() => expectToFail(() => ng('build')))

// Set an exception for the invalid asset config in .angular-cli.json.
.then(() => updateJsonFile('.angular-cli.json', configJson => {
const app = configJson['apps'][0];
app['assets'] = [
{ 'glob': '**/*', 'input': '../node_modules/some-package/', 'output': '../temp',
'allowOutsideOutDir': true }
];
}))
.then(() => ng('build'))

// This asset should fail even with the exception above.
.then(() => updateJsonFile('.angular-cli.json', configJson => {
const app = configJson['apps'][0];
app['assets'] = [
{ 'glob': '**/*', 'input': '../node_modules/some-package/', 'output': '../../temp',
'allowOutsideOutDir': true }
];
}))
.then(() => expectToFail(() => ng('build')))

// This asset should also fail from reading from outside the project.
.then(() => updateJsonFile('.angular-cli.json', configJson => {
const app = configJson['apps'][0];
app['assets'] = [
{ 'glob': '**/*', 'input': '/temp-folder/outside/of/project', 'output': 'temp' }
];
}))
.then(() => expectToFail(() => ng('build')))

// Add asset config in .angular-cli.json.
.then(() => updateJsonFile('.angular-cli.json', configJson => {
const app = configJson['apps'][0];
Expand Down