`source/_data` YAML can define executable JS via default `!!js/function` (`js-yaml-js-types`.all)
- Dominant language
- TypeScript
- Stars
- 41.8k
- Forks
- 41
- PR merge metrics
- No merged PRs in 30d
Description
### Check List
- [x] I have already read [Docs page](https://hexo.io/docs/) & [Troubleshooting page](https://hexo.io/docs/troubleshooting).
- [x] I have already searched existing issues and they are not help to me.
- [x] I examined error or warning messages and it's difficult to solve.
- [x] I am using the [latest](https://github.com/hexojs/hexo/releases) version of Hexo. (run `hexo version` to check)
- [x] My Node.js is matched [the required version](https://hexo.io/docs/#Required-Node-js-version).
### Expected behavior
YAML files under `source/_data/` should be parsed as plain data. Default parsing should not construct JavaScript functions from YAML tags.
If `!!js/regexp` / `!!js/undefined` are still needed, only those should be enabled — not `!!js/function` by default.
### Actual behavior
Hexo’s YAML renderer enables `js-yaml-js-types`.all by default:
https://github.com/hexojs/hexo/blob/master/lib/plugins/renderer/yaml.ts
```js
schema = yaml.DEFAULT_SCHEMA.extend(require('js-yaml-js-types').all);
```
That includes `!!js/function`. A `source/_data/*.yml` file can define a real Function (for example on `toString`). When a theme renders `{{ site.data.menu }}`, Nunjucks stringifies the object, calls that function, and arbitrary Node.js code runs in the Hexo process.
`js-yaml` v4 made these tags opt-in; re-enabling `.all` by default is unexpected for site data files.
### How to reproduce?
1. Create a minimal Hexo site with `hexo@8.1.2` and a theme layout containing:
```njk
{{ site.data.menu }}
```
2. Create `source/_data/menu.yml`:
```yaml
toString: !!js/function 'function (){ process.getBuiltinModule("fs").writeFileSync("/tmp/hexo_js_function","1"); return ""; }'
Home: /
```
3. Load the site and render that template (same path themes use when printing `site.data`):
```js
const Hexo = require('hexo');
const nunjucks = require(require.resolve('nunjucks', { paths: [require.resolve('hexo')] }));
(async () => {
const hexo = new Hexo(process.cwd(), { silent: true });
await hexo.init();
await hexo.load();
const menu = hexo.locals.get('data').menu;
console.log(typeof menu.toString); // function
nunjucks.renderString('{{ site.data.menu }}', { site: { data: { menu } } });
})();
```
4. Observe that `/tmp/hexo_js_function` is created with contents `1`.
### Is the problem still there under `Safe mode`?
Yes. This comes from Hexo’s built-in YAML renderer (`lib/plugins/renderer/yaml.ts`), not from a third-party plugin/script. Disabling plugins does not remove this schema extension.
### Your Node.js & npm version
```text
node v22.22.0
npm 9.2.0
```
### Your Hexo and Plugin version
```text
hexo@8.1.2
```
### Your `package.json`
```json
{
"name": "hexo-yaml-jsfunction-repro",
"private": true,
"hexo": {},
"dependencies": {
"hexo": "8.1.2"
}
}
```
### Your site's `_config.yml` (Optional)
```yaml
```
### Others
**Suggested fix**
- Do not use `js-yaml-js-types`.all by default.
- Keep the default safe schema, or extend only `regexp` / `undefined` if still required.
- If `!!js/function` must remain for compatibility, make it an explicit opt-in (default off).
Contributor guide
Research direction
Start with lib/plugins/renderer/yaml.ts and reproduce the issue using the minimal Hexo site, source/_data/menu.yml, and Nunjucks render shown in the report. Confirm that default YAML parsing no longer creates a JavaScript function while checking whether the reported regexp and undefined tags still work or require explicit opt-in.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, nodejs, typescript
- Domain
- security
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100