@backstage/plugin-techdocs-node 1.12.11-next.1 → 1.12.11
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/CHANGELOG.md +37 -0
- package/dist/index.cjs.js +87 -4
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +164 -165
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# @backstage/plugin-techdocs-node
|
|
2
2
|
|
|
3
|
+
## 1.12.11
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- f715f5c: Move `TechdocsContainerRunner` from `publish` to `generate`.
|
|
8
|
+
- 4417dd4: Fix typo and unify TechDocs casing in doc strings
|
|
9
|
+
- 57da51f: Add support for mapping custom tags in the techdocs yaml parser that validates the mkdocs.yaml file
|
|
10
|
+
- c2b63ab: Updated dependency `supertest` to `^7.0.0`.
|
|
11
|
+
- 3606843: Internal fixes to match `testcontainers` update
|
|
12
|
+
- 33ebb28: As the `@backstage/backend-common` package is deprecated, we have updated the `techdocs-node` package to stop depending on it.
|
|
13
|
+
- Updated dependencies
|
|
14
|
+
- @backstage/backend-plugin-api@1.0.0
|
|
15
|
+
- @backstage/catalog-model@1.7.0
|
|
16
|
+
- @backstage/integration@1.15.0
|
|
17
|
+
- @backstage/config@1.2.0
|
|
18
|
+
- @backstage/errors@1.2.4
|
|
19
|
+
- @backstage/integration-aws-node@0.1.12
|
|
20
|
+
- @backstage/plugin-search-common@1.2.14
|
|
21
|
+
- @backstage/plugin-techdocs-common@0.1.0
|
|
22
|
+
|
|
23
|
+
## 1.12.11-next.2
|
|
24
|
+
|
|
25
|
+
### Patch Changes
|
|
26
|
+
|
|
27
|
+
- 57da51f: Add support for mapping custom tags in the techdocs yaml parser that validates the mkdocs.yaml file
|
|
28
|
+
- c2b63ab: Updated dependency `supertest` to `^7.0.0`.
|
|
29
|
+
- 3606843: Internal fixes to match `testcontainers` update
|
|
30
|
+
- Updated dependencies
|
|
31
|
+
- @backstage/backend-plugin-api@1.0.0-next.2
|
|
32
|
+
- @backstage/integration@1.15.0-next.0
|
|
33
|
+
- @backstage/catalog-model@1.6.0
|
|
34
|
+
- @backstage/config@1.2.0
|
|
35
|
+
- @backstage/errors@1.2.4
|
|
36
|
+
- @backstage/integration-aws-node@0.1.12
|
|
37
|
+
- @backstage/plugin-search-common@1.2.14
|
|
38
|
+
- @backstage/plugin-techdocs-common@0.1.0
|
|
39
|
+
|
|
3
40
|
## 1.12.11-next.1
|
|
4
41
|
|
|
5
42
|
### Patch Changes
|
package/dist/index.cjs.js
CHANGED
|
@@ -47,7 +47,29 @@ var os__default = /*#__PURE__*/_interopDefaultCompat(os);
|
|
|
47
47
|
|
|
48
48
|
const getContentTypeForExtension = (ext) => {
|
|
49
49
|
const defaultContentType = "text/plain; charset=utf-8";
|
|
50
|
-
|
|
50
|
+
const excludedTypes = [
|
|
51
|
+
"text/html",
|
|
52
|
+
"text/xml",
|
|
53
|
+
"image/svg+xml",
|
|
54
|
+
"text/xsl",
|
|
55
|
+
"application/vnd.wap.xhtml+xml",
|
|
56
|
+
"multipart/x-mixed-replace",
|
|
57
|
+
"text/rdf",
|
|
58
|
+
"application/mathml+xml",
|
|
59
|
+
"application/octet-stream",
|
|
60
|
+
"application/rdf+xml",
|
|
61
|
+
"application/xhtml+xml",
|
|
62
|
+
"application/xml",
|
|
63
|
+
"text/cache-manifest",
|
|
64
|
+
"text/vtt"
|
|
65
|
+
];
|
|
66
|
+
if (ext.match(
|
|
67
|
+
/htm|xml|svg|appcache|manifest|mathml|owl|rdf|rng|vtt|xht|xsd|xsl/i
|
|
68
|
+
)) {
|
|
69
|
+
return defaultContentType;
|
|
70
|
+
}
|
|
71
|
+
const contentType = mime__default.default.lookup(ext);
|
|
72
|
+
if (contentType && excludedTypes.includes(contentType)) {
|
|
51
73
|
return defaultContentType;
|
|
52
74
|
}
|
|
53
75
|
return mime__default.default.contentType(ext) || defaultContentType;
|
|
@@ -126,6 +148,15 @@ const bulkStorageOperation = async (operation, args, { concurrencyLimit } = { co
|
|
|
126
148
|
const limiter = createLimiter__default.default(concurrencyLimit);
|
|
127
149
|
await Promise.all(args.map((arg) => limiter(operation, arg)));
|
|
128
150
|
};
|
|
151
|
+
const isValidContentPath = (bucketRoot, contentPath) => {
|
|
152
|
+
const relativePath = path__default.default.posix.relative(bucketRoot, contentPath);
|
|
153
|
+
if (relativePath === "") {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
const outsideBase = relativePath.startsWith("..");
|
|
157
|
+
const differentDrive = path__default.default.posix.isAbsolute(relativePath);
|
|
158
|
+
return !outsideBase && !differentDrive;
|
|
159
|
+
};
|
|
129
160
|
|
|
130
161
|
function getGeneratorKey(entity) {
|
|
131
162
|
if (!entity) {
|
|
@@ -196,6 +227,14 @@ const MKDOCS_SCHEMA = yaml.DEFAULT_SCHEMA.extend([
|
|
|
196
227
|
instanceOf: UnknownTag,
|
|
197
228
|
construct: (data, type) => new UnknownTag(data, type)
|
|
198
229
|
}),
|
|
230
|
+
new yaml.Type("tag:", {
|
|
231
|
+
kind: "mapping",
|
|
232
|
+
multi: true,
|
|
233
|
+
representName: (o) => o.type,
|
|
234
|
+
represent: (o) => o.data ?? "",
|
|
235
|
+
instanceOf: UnknownTag,
|
|
236
|
+
construct: (data, type) => new UnknownTag(data, type)
|
|
237
|
+
}),
|
|
199
238
|
new yaml.Type("", {
|
|
200
239
|
kind: "sequence",
|
|
201
240
|
multi: true,
|
|
@@ -466,9 +505,15 @@ class DockerContainerRunner {
|
|
|
466
505
|
this.dockerClient.pull(imageName, {}, (err, stream) => {
|
|
467
506
|
if (err) {
|
|
468
507
|
reject(err);
|
|
469
|
-
|
|
508
|
+
} else if (!stream) {
|
|
509
|
+
reject(
|
|
510
|
+
new Error(
|
|
511
|
+
"Unexpeected error: no stream returned from Docker while pulling image"
|
|
512
|
+
)
|
|
513
|
+
);
|
|
514
|
+
} else {
|
|
515
|
+
pipeline(stream, logStream, { end: false }).then(resolve).catch(reject);
|
|
470
516
|
}
|
|
471
|
-
pipeline(stream, logStream, { end: false }).then(resolve).catch(reject);
|
|
472
517
|
});
|
|
473
518
|
});
|
|
474
519
|
}
|
|
@@ -485,7 +530,7 @@ class DockerContainerRunner {
|
|
|
485
530
|
const realHostDir = await fs__default.default.realpath(hostDir);
|
|
486
531
|
Binds.push(`${realHostDir}:${containerDir}`);
|
|
487
532
|
}
|
|
488
|
-
const Env =
|
|
533
|
+
const Env = new Array();
|
|
489
534
|
for (const [key, value] of Object.entries(envVars)) {
|
|
490
535
|
Env.push(`${key}=${value}`);
|
|
491
536
|
}
|
|
@@ -1150,6 +1195,12 @@ class AwsS3Publish {
|
|
|
1150
1195
|
const entityTriplet = `${entityName.namespace}/${entityName.kind}/${entityName.name}`;
|
|
1151
1196
|
const entityDir = this.legacyPathCasing ? entityTriplet : lowerCaseEntityTriplet(entityTriplet);
|
|
1152
1197
|
const entityRootDir = path__default.default.posix.join(this.bucketRootPath, entityDir);
|
|
1198
|
+
if (!isValidContentPath(this.bucketRootPath, entityRootDir)) {
|
|
1199
|
+
this.logger.error(
|
|
1200
|
+
`Invalid content path found while fetching TechDocs metadata: ${entityRootDir}`
|
|
1201
|
+
);
|
|
1202
|
+
throw new Error(`Metadata Not Found`);
|
|
1203
|
+
}
|
|
1153
1204
|
try {
|
|
1154
1205
|
const resp = await this.storageClient.send(
|
|
1155
1206
|
new clientS3.GetObjectCommand({
|
|
@@ -1187,6 +1238,13 @@ class AwsS3Publish {
|
|
|
1187
1238
|
const decodedUri = decodeURI(req.path.replace(/^\//, ""));
|
|
1188
1239
|
const filePathNoRoot = this.legacyPathCasing ? decodedUri : lowerCaseEntityTripletInStoragePath(decodedUri);
|
|
1189
1240
|
const filePath = path__default.default.posix.join(this.bucketRootPath, filePathNoRoot);
|
|
1241
|
+
if (!isValidContentPath(this.bucketRootPath, filePath)) {
|
|
1242
|
+
this.logger.error(
|
|
1243
|
+
`Attempted to fetch TechDocs content for a file outside of the bucket root: ${filePathNoRoot}`
|
|
1244
|
+
);
|
|
1245
|
+
res.status(404).send("File Not Found");
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1190
1248
|
const fileExtension = path__default.default.extname(filePath);
|
|
1191
1249
|
const responseHeaders = getHeadersForFileExtension(fileExtension);
|
|
1192
1250
|
try {
|
|
@@ -1217,6 +1275,12 @@ class AwsS3Publish {
|
|
|
1217
1275
|
const entityTriplet = `${entity.metadata.namespace}/${entity.kind}/${entity.metadata.name}`;
|
|
1218
1276
|
const entityDir = this.legacyPathCasing ? entityTriplet : lowerCaseEntityTriplet(entityTriplet);
|
|
1219
1277
|
const entityRootDir = path__default.default.posix.join(this.bucketRootPath, entityDir);
|
|
1278
|
+
if (!isValidContentPath(this.bucketRootPath, entityRootDir)) {
|
|
1279
|
+
this.logger.error(
|
|
1280
|
+
`Invalid content path found while checking if docs have been generated: ${entityRootDir}`
|
|
1281
|
+
);
|
|
1282
|
+
return Promise.resolve(false);
|
|
1283
|
+
}
|
|
1220
1284
|
await this.storageClient.send(
|
|
1221
1285
|
new clientS3.HeadObjectCommand({
|
|
1222
1286
|
Bucket: this.bucketName,
|
|
@@ -1829,6 +1893,12 @@ class GoogleGCSPublish {
|
|
|
1829
1893
|
const entityTriplet = `${entityName.namespace}/${entityName.kind}/${entityName.name}`;
|
|
1830
1894
|
const entityDir = this.legacyPathCasing ? entityTriplet : lowerCaseEntityTriplet(entityTriplet);
|
|
1831
1895
|
const entityRootDir = path__default.default.posix.join(this.bucketRootPath, entityDir);
|
|
1896
|
+
if (!isValidContentPath(this.bucketRootPath, entityRootDir)) {
|
|
1897
|
+
this.logger.error(
|
|
1898
|
+
`Invalid content path found while fetching TechDocs metadata: ${entityRootDir}`
|
|
1899
|
+
);
|
|
1900
|
+
reject(new Error(`Metadata Not Found`));
|
|
1901
|
+
}
|
|
1832
1902
|
const fileStreamChunks = [];
|
|
1833
1903
|
this.storageClient.bucket(this.bucketName).file(`${entityRootDir}/techdocs_metadata.json`).createReadStream().on("error", (err) => {
|
|
1834
1904
|
this.logger.error(err.message);
|
|
@@ -1849,6 +1919,13 @@ class GoogleGCSPublish {
|
|
|
1849
1919
|
const decodedUri = decodeURI(req.path.replace(/^\//, ""));
|
|
1850
1920
|
const filePathNoRoot = this.legacyPathCasing ? decodedUri : lowerCaseEntityTripletInStoragePath(decodedUri);
|
|
1851
1921
|
const filePath = path__default.default.posix.join(this.bucketRootPath, filePathNoRoot);
|
|
1922
|
+
if (!isValidContentPath(this.bucketRootPath, filePath)) {
|
|
1923
|
+
this.logger.error(
|
|
1924
|
+
`Attempted to fetch TechDocs content for a file outside of the bucket root: ${filePathNoRoot}`
|
|
1925
|
+
);
|
|
1926
|
+
res.status(404).send("File Not Found");
|
|
1927
|
+
return;
|
|
1928
|
+
}
|
|
1852
1929
|
const fileExtension = path__default.default.extname(filePath);
|
|
1853
1930
|
const responseHeaders = getHeadersForFileExtension(fileExtension);
|
|
1854
1931
|
this.storageClient.bucket(this.bucketName).file(filePath).createReadStream().on("pipe", () => {
|
|
@@ -1874,6 +1951,12 @@ class GoogleGCSPublish {
|
|
|
1874
1951
|
const entityTriplet = `${entity.metadata.namespace}/${entity.kind}/${entity.metadata.name}`;
|
|
1875
1952
|
const entityDir = this.legacyPathCasing ? entityTriplet : lowerCaseEntityTriplet(entityTriplet);
|
|
1876
1953
|
const entityRootDir = path__default.default.posix.join(this.bucketRootPath, entityDir);
|
|
1954
|
+
if (!isValidContentPath(this.bucketRootPath, entityRootDir)) {
|
|
1955
|
+
this.logger.error(
|
|
1956
|
+
`Invalid content path found while checking if docs have been generated: ${entityRootDir}`
|
|
1957
|
+
);
|
|
1958
|
+
resolve(false);
|
|
1959
|
+
}
|
|
1877
1960
|
this.storageClient.bucket(this.bucketName).file(`${entityRootDir}/index.html`).exists().then((response) => {
|
|
1878
1961
|
resolve(response[0]);
|
|
1879
1962
|
}).catch(() => {
|