Skip to content

Commit 042abdb

Browse files
fix(core): Prevent exposure of private custom fields via JSON type
Fixes #3049
1 parent 7ae064c commit 042abdb

File tree

2 files changed

+37
-10
lines changed

2 files changed

+37
-10
lines changed

packages/core/e2e/custom-fields.e2e-spec.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,10 @@ const customConfig = mergeConfig(testConfig(), {
183183
readonly: true,
184184
},
185185
],
186+
Collection: [
187+
{ name: 'secretKey1', type: 'string', defaultValue: '', public: false, internal: true },
188+
{ name: 'secretKey2', type: 'string', defaultValue: '', public: false, internal: false },
189+
],
186190
OrderLine: [{ name: 'validateInt', type: 'int', min: 0, max: 10 }],
187191
} as CustomFields,
188192
});
@@ -942,6 +946,20 @@ describe('Custom fields', () => {
942946
`);
943947
}, 'Cannot query field "internalString" on type "ProductCustomFields"'),
944948
);
949+
950+
// https://github.com/vendure-ecommerce/vendure/issues/3049
951+
it('does not leak private fields via JSON type', async () => {
952+
const { collection } = await shopClient.query(gql`
953+
query {
954+
collection(id: "T_1") {
955+
id
956+
customFields
957+
}
958+
}
959+
`);
960+
961+
expect(collection.customFields).toBe(null);
962+
});
945963
});
946964

947965
describe('sort & filter', () => {
@@ -1087,18 +1105,16 @@ describe('Custom fields', () => {
10871105

10881106
describe('unique constraint', () => {
10891107
it('setting unique value works', async () => {
1090-
const result = await adminClient.query(
1091-
gql`
1092-
mutation {
1093-
updateProduct(input: { id: "T_1", customFields: { uniqueString: "foo" } }) {
1094-
id
1095-
customFields {
1096-
uniqueString
1097-
}
1108+
const result = await adminClient.query(gql`
1109+
mutation {
1110+
updateProduct(input: { id: "T_1", customFields: { uniqueString: "foo" } }) {
1111+
id
1112+
customFields {
1113+
uniqueString
10981114
}
10991115
}
1100-
`,
1101-
);
1116+
}
1117+
`);
11021118

11031119
expect(result.updateProduct.customFields.uniqueString).toBe('foo');
11041120
});

packages/core/src/api/config/generate-resolvers.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,17 @@ function generateCustomFieldRelationResolvers(
255255
} as any;
256256
}
257257
}
258+
const allCustomFieldsAreNonPublic = customFields.every(
259+
f => f.public === false || f.internal === true,
260+
);
261+
if (allCustomFieldsAreNonPublic) {
262+
// When an entity has only non-public custom fields, the GraphQL type used for the
263+
// customFields field is `JSON`. This type will simply return the full object, which
264+
// will cause a leak of private data unless we force a `null` return value in the case
265+
// that there are no public fields.
266+
// See https://github.com/vendure-ecommerce/vendure/issues/3049
267+
shopResolvers[entityName] = { customFields: () => null };
268+
}
258269
}
259270
return { adminResolvers, shopResolvers };
260271
}

0 commit comments

Comments
 (0)