Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 7 additions & 0 deletions .changeset/harden-object-path-lookups.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'astro': patch
'@astrojs/markdown-remark': patch
'create-astro': patch
---

Hardens nested object and package metadata lookups to ignore prototype keys in content handling and project scaffolding
8 changes: 8 additions & 0 deletions packages/astro/src/core/base-pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import type { SessionDriverFactory } from './session/types.js';
import { NodePool } from '../runtime/server/render/queue/pool.js';
import { HTMLStringCache } from '../runtime/server/html-string-cache.js';

const FORBIDDEN_ACTION_PATH_KEYS = new Set(['__proto__', 'constructor', 'prototype']);

/**
* The `Pipeline` represents the static parts of rendering that do not change between requests.
* These are mostly known when the server first starts up and do not change.
Expand Down Expand Up @@ -287,6 +289,12 @@ export abstract class Pipeline {
}

for (const key of pathKeys) {
if (FORBIDDEN_ACTION_PATH_KEYS.has(key)) {
throw new AstroError({
...ActionNotFoundError,
message: ActionNotFoundError.message(pathKeys.join('.')),
});
}
if (!Object.hasOwn(server, key)) {
throw new AstroError({
...ActionNotFoundError,
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/src/core/cache/memory-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ function parseVaryHeader(response: Response): string[] | undefined {
* Extract the values of Vary'd headers from a request.
*/
function getVaryValues(request: Request, varyHeaders: string[]): Record<string, string> {
const values: Record<string, string> = {};
const values = Object.create(null) as Record<string, string>;
Comment thread
ematipico marked this conversation as resolved.
for (const header of varyHeaders) {
values[header] = request.headers.get(header) ?? '';
}
Expand Down
7 changes: 6 additions & 1 deletion packages/astro/src/preferences/dlv.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
const FORBIDDEN_PATH_KEYS = new Set(['__proto__', 'constructor', 'prototype']);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't repeat the same object. Maybe move it inside internal-helpers so we can always use the same set.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved this into @astrojs/internal-helpers/object


export default function dlv(obj: Record<string, unknown>, key: string): any {
for (const k of key.split('.')) {
if (FORBIDDEN_PATH_KEYS.has(k) || !obj || typeof obj !== 'object' || !Object.hasOwn(obj, k)) {
return undefined;
}
// @ts-expect-error: Type 'unknown' is not assignable to type 'Record<string, unknown>'.
obj = obj?.[k];
obj = obj[k];
}
return obj;
}
13 changes: 4 additions & 9 deletions packages/create-astro/src/actions/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,10 @@ const FILES_TO_UPDATE = {
fs.promises.readFile(file, 'utf-8').then((value) => {
// Match first indent in the file or fall back to `\t`
const indent = /(^\s+)/m.exec(value)?.[1] ?? '\t';
return fs.promises.writeFile(
file,
JSON.stringify(
Object.assign(JSON.parse(value), Object.assign(overrides, { private: undefined })),
null,
indent,
),
'utf-8',
);
const packageJson = JSON.parse(value);
packageJson.name = overrides.name;
delete packageJson.private;
return fs.promises.writeFile(file, JSON.stringify(packageJson, null, indent), 'utf-8');
}),
};

Expand Down
10 changes: 9 additions & 1 deletion packages/markdown/remark/src/rehype-collect-headings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { MarkdownHeading, RehypePlugin } from './types.js';

const rawNodeTypes = new Set(['text', 'raw', 'mdxTextExpression']);
const codeTagNames = new Set(['code', 'pre']);
const FORBIDDEN_FRONTMATTER_KEYS = new Set(['__proto__', 'constructor', 'prototype']);

/**
* Rehype plugin that adds `id` attributes to headings based on their text content.
Expand Down Expand Up @@ -117,7 +118,14 @@ function getMdxFrontmatterVariableValue(frontmatter: Record<string, any>, path:
let value = frontmatter;

for (const key of path) {
if (!value[key]) return undefined;
if (
FORBIDDEN_FRONTMATTER_KEYS.has(key) ||
!value ||
typeof value !== 'object' ||
!Object.hasOwn(value, key)
) {
return undefined;
}

value = value[key];
}
Expand Down
Loading