@cronn/playwright-utils 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +47 -30
- package/package.json +6 -6
package/dist/index.d.mts
CHANGED
|
@@ -618,9 +618,9 @@ export declare function getCspHeader(response: Response | null): Promise<string>
|
|
|
618
618
|
*/
|
|
619
619
|
export declare function cspReport(header: string): string;
|
|
620
620
|
//#endregion
|
|
621
|
-
//#region src/security/response-
|
|
621
|
+
//#region src/security/response-headers-report.d.ts
|
|
622
622
|
type ResponseHeaderType = "document" | "api";
|
|
623
|
-
interface
|
|
623
|
+
interface ResponseHeadersReportOptions {
|
|
624
624
|
type: ResponseHeaderType;
|
|
625
625
|
}
|
|
626
626
|
/**
|
|
@@ -643,12 +643,12 @@ export declare function getResponseHeaders(response: Response | APIResponse | nu
|
|
|
643
643
|
* const response = await page.goto("/");
|
|
644
644
|
* const headers = await getResponseHeaders(response);
|
|
645
645
|
*
|
|
646
|
-
* expect(
|
|
646
|
+
* expect(responseHeadersReport(headers, { type: "document" })).toMatchTextFile({
|
|
647
647
|
* fileExtension: "md",
|
|
648
648
|
* });
|
|
649
649
|
* ```
|
|
650
650
|
*/
|
|
651
|
-
export declare function
|
|
651
|
+
export declare function responseHeadersReport(headers: Record<string, string>, options: ResponseHeadersReportOptions): string;
|
|
652
652
|
//#endregion
|
|
653
653
|
//#region src/file.d.ts
|
|
654
654
|
/**
|
|
@@ -789,4 +789,4 @@ export declare function maskedValueWithIndex(name: string): (index: number) => s
|
|
|
789
789
|
*/
|
|
790
790
|
type PlaywrightTarget = Page | Locator;
|
|
791
791
|
//#endregion
|
|
792
|
-
export type { ConsoleMessageFilter, ConsoleMessageLevel, CspFixtures, CspViolation, ExtendedLocator, FormFields, HttpMethod, PlaywrightTarget, ResponseHandler,
|
|
792
|
+
export type { ConsoleMessageFilter, ConsoleMessageLevel, CspFixtures, CspViolation, ExtendedLocator, FormFields, HttpMethod, PlaywrightTarget, ResponseHandler, ResponseHeaderType, ResponseHeadersReportOptions, RoleLocators, RouteFilter };
|
package/dist/index.mjs
CHANGED
|
@@ -1028,7 +1028,7 @@ function formatValues(values) {
|
|
|
1028
1028
|
return codeOrNone(values.join(" "));
|
|
1029
1029
|
}
|
|
1030
1030
|
//#endregion
|
|
1031
|
-
//#region src/security/response-
|
|
1031
|
+
//#region src/security/response-headers-report.ts
|
|
1032
1032
|
const ACTION_ORDER = [
|
|
1033
1033
|
"add",
|
|
1034
1034
|
"change",
|
|
@@ -1036,32 +1036,35 @@ const ACTION_ORDER = [
|
|
|
1036
1036
|
];
|
|
1037
1037
|
const MIN_HSTS_MAX_AGE = 31536e3;
|
|
1038
1038
|
const PERMISSIONS_POLICY = "geolocation=(), microphone=(), camera=()";
|
|
1039
|
-
const
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
name: "
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1039
|
+
const API_CSP = "frame-ancestors 'none'";
|
|
1040
|
+
function recommendedCommonHeaders(csp) {
|
|
1041
|
+
return [
|
|
1042
|
+
{ name: "cache-control" },
|
|
1043
|
+
csp,
|
|
1044
|
+
{ name: "content-type" },
|
|
1045
|
+
{
|
|
1046
|
+
name: "cross-origin-opener-policy",
|
|
1047
|
+
recommended: "same-origin"
|
|
1048
|
+
},
|
|
1049
|
+
{
|
|
1050
|
+
name: "cross-origin-resource-policy",
|
|
1051
|
+
recommended: "same-origin"
|
|
1052
|
+
},
|
|
1053
|
+
{
|
|
1054
|
+
name: "strict-transport-security",
|
|
1055
|
+
recommended: `max-age=${MIN_HSTS_MAX_AGE}; includeSubDomains`,
|
|
1056
|
+
isAccepted: isStrictHsts
|
|
1057
|
+
},
|
|
1058
|
+
{
|
|
1059
|
+
name: "x-content-type-options",
|
|
1060
|
+
recommended: "nosniff"
|
|
1061
|
+
},
|
|
1062
|
+
{ name: "x-robots-tag" }
|
|
1063
|
+
];
|
|
1064
|
+
}
|
|
1062
1065
|
const DISCOURAGED_COMMON_HEADERS = ["access-control-allow-origin"];
|
|
1063
1066
|
const RECOMMENDED_DOCUMENT_HEADERS = [
|
|
1064
|
-
...
|
|
1067
|
+
...recommendedCommonHeaders({ name: "content-security-policy" }),
|
|
1065
1068
|
{
|
|
1066
1069
|
name: "permissions-policy",
|
|
1067
1070
|
recommended: PERMISSIONS_POLICY,
|
|
@@ -1076,10 +1079,15 @@ const RECOMMENDED_DOCUMENT_HEADERS = [
|
|
|
1076
1079
|
recommended: "DENY"
|
|
1077
1080
|
}
|
|
1078
1081
|
];
|
|
1082
|
+
const RECOMMENDED_API_HEADERS = recommendedCommonHeaders({
|
|
1083
|
+
name: "content-security-policy",
|
|
1084
|
+
recommended: API_CSP,
|
|
1085
|
+
isAccepted: hasApiCspDirectives
|
|
1086
|
+
});
|
|
1079
1087
|
const DISCOURAGED_DOCUMENT_HEADERS = [...DISCOURAGED_COMMON_HEADERS, "x-xss-protection"];
|
|
1080
1088
|
const RECOMMENDED_HEADERS = {
|
|
1081
1089
|
document: RECOMMENDED_DOCUMENT_HEADERS,
|
|
1082
|
-
api:
|
|
1090
|
+
api: RECOMMENDED_API_HEADERS
|
|
1083
1091
|
};
|
|
1084
1092
|
const DISCOURAGED_HEADERS = {
|
|
1085
1093
|
document: DISCOURAGED_DOCUMENT_HEADERS,
|
|
@@ -1108,12 +1116,12 @@ async function getResponseHeaders(response) {
|
|
|
1108
1116
|
* const response = await page.goto("/");
|
|
1109
1117
|
* const headers = await getResponseHeaders(response);
|
|
1110
1118
|
*
|
|
1111
|
-
* expect(
|
|
1119
|
+
* expect(responseHeadersReport(headers, { type: "document" })).toMatchTextFile({
|
|
1112
1120
|
* fileExtension: "md",
|
|
1113
1121
|
* });
|
|
1114
1122
|
* ```
|
|
1115
1123
|
*/
|
|
1116
|
-
function
|
|
1124
|
+
function responseHeadersReport(headers, options) {
|
|
1117
1125
|
const recommendedHeaders = RECOMMENDED_HEADERS[options.type];
|
|
1118
1126
|
const discouragedHeaders = DISCOURAGED_HEADERS[options.type];
|
|
1119
1127
|
const headersByName = new Map(Object.entries(headers).map(([name, value]) => [name.toLowerCase(), value]));
|
|
@@ -1129,7 +1137,7 @@ function responseHeaderReport(headers, options) {
|
|
|
1129
1137
|
action: "remove",
|
|
1130
1138
|
message: "Remove header"
|
|
1131
1139
|
}))].sort((a, b) => ACTION_ORDER.indexOf(a.action) - ACTION_ORDER.indexOf(b.action));
|
|
1132
|
-
return markdownDocument("Response
|
|
1140
|
+
return markdownDocument("Response Headers Report", [{
|
|
1133
1141
|
heading: "Detected Headers",
|
|
1134
1142
|
content: markdownTableOrNone(["Header", "Value"], detectedHeaders.map((header) => [code(header.name), formatValue(header)]))
|
|
1135
1143
|
}, {
|
|
@@ -1164,6 +1172,15 @@ function isStrictHsts(value) {
|
|
|
1164
1172
|
return maxAge !== void 0 && Number(maxAge) >= MIN_HSTS_MAX_AGE && directives.includes("includesubdomains");
|
|
1165
1173
|
}
|
|
1166
1174
|
/**
|
|
1175
|
+
* Accepts a policy if it contains the recommended directives, so stricter
|
|
1176
|
+
* policies with further directives like `default-src 'none'` are accepted as
|
|
1177
|
+
* well.
|
|
1178
|
+
*/
|
|
1179
|
+
function hasApiCspDirectives(value) {
|
|
1180
|
+
const actualDirectives = normalizeValue(value).split("; ");
|
|
1181
|
+
return normalizeValue(API_CSP).split("; ").every((directive) => actualDirectives.includes(directive));
|
|
1182
|
+
}
|
|
1183
|
+
/**
|
|
1167
1184
|
* Accepts a policy if it restricts at least the recommended permissions, so
|
|
1168
1185
|
* stricter policies restricting further permissions are accepted as well.
|
|
1169
1186
|
*/
|
|
@@ -1420,4 +1437,4 @@ function identity(value) {
|
|
|
1420
1437
|
return value;
|
|
1421
1438
|
}
|
|
1422
1439
|
//#endregion
|
|
1423
|
-
export { ConsoleCaptor, CspCaptor, RouteInterceptor, RouteInterceptorFixture, captureConsole, captureCspViolations, createFetchAdapter, cspFixtures, cspReport, extendLocator, formFiller, getCspHeader, getResponseHeaders, hasLogLevel, interceptRoute, isCI, maskBaseURL, maskedValue, maskedValueWithIndex, matchPath, modifyJsonBody, modifyTextBody, resolveFromPackageRoot,
|
|
1440
|
+
export { ConsoleCaptor, CspCaptor, RouteInterceptor, RouteInterceptorFixture, captureConsole, captureCspViolations, createFetchAdapter, cspFixtures, cspReport, extendLocator, formFiller, getCspHeader, getResponseHeaders, hasLogLevel, interceptRoute, isCI, maskBaseURL, maskedValue, maskedValueWithIndex, matchPath, modifyJsonBody, modifyTextBody, resolveFromPackageRoot, responseHeadersReport, roleLocators };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cronn/playwright-utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Utilities for writing tests with Playwright",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"playwright",
|
|
@@ -37,25 +37,25 @@
|
|
|
37
37
|
"@changesets/cli": "3.0.3",
|
|
38
38
|
"@cronn/element-snapshot": "0.32.1",
|
|
39
39
|
"@cronn/playwright-file-snapshots": "2.2.2",
|
|
40
|
-
"@playwright/test": "1.
|
|
41
|
-
"@types/node": "24.19.
|
|
40
|
+
"@playwright/test": "1.64.0",
|
|
41
|
+
"@types/node": "24.19.2",
|
|
42
42
|
"@typescript/native": "npm:typescript@^7.0.2",
|
|
43
43
|
"@vitest/coverage-v8": "5.0.3",
|
|
44
44
|
"eslint": "10.12.0",
|
|
45
45
|
"eslint-config-prettier": "10.1.8",
|
|
46
46
|
"eslint-plugin-check-file": "3.3.2",
|
|
47
47
|
"eslint-plugin-unused-imports": "4.4.1",
|
|
48
|
-
"knip": "6.
|
|
48
|
+
"knip": "6.41.0",
|
|
49
49
|
"oxc-minify": "0.153.0",
|
|
50
50
|
"oxfmt": "0.72.0",
|
|
51
51
|
"publint": "0.3.25",
|
|
52
52
|
"rimraf": "6.1.3",
|
|
53
|
-
"tsdown": "0.23.
|
|
53
|
+
"tsdown": "0.23.1",
|
|
54
54
|
"turbo": "2.11.7",
|
|
55
55
|
"typescript": "npm:@typescript/typescript6@^6.0.2",
|
|
56
56
|
"typescript-eslint": "8.71.1",
|
|
57
57
|
"vitepress": "2.0.0-alpha.20",
|
|
58
|
-
"vitepress-plugin-group-icons": "1.7.
|
|
58
|
+
"vitepress-plugin-group-icons": "1.7.7",
|
|
59
59
|
"vitest": "5.0.3"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|