@sap-ux/fe-fpm-writer 1.1.6 → 1.2.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.
@@ -31,4 +31,13 @@ export declare function validateBasePath(basePath: string, fs?: Editor, dependen
31
31
  * @returns {string} the error message
32
32
  */
33
33
  export declare function getErrorMessage(error: Error | unknown): string;
34
+ /**
35
+ * Validates that a folder path is relative and does not contain path traversal sequences.
36
+ * This prevents security issues where malicious paths like '../../../etc' could write files outside the project.
37
+ *
38
+ * @param {string} folder - the folder path to validate
39
+ * @param {string} [paramName] - optional parameter name for error messages
40
+ * @throws {Error} if the path is absolute or contains '..'
41
+ */
42
+ export declare function validateRelativePath(folder: string, paramName?: string): void;
34
43
  //# sourceMappingURL=validate.d.ts.map
@@ -1,6 +1,7 @@
1
1
  import { create as createStorage } from 'mem-fs';
2
2
  import { create } from 'mem-fs-editor';
3
3
  import { coerce, lt } from 'semver';
4
+ import { isAbsolute } from 'node:path';
4
5
  import { getManifestPath } from './utils.js';
5
6
  /**
6
7
  * Validate that the UI5 version requirement is valid.
@@ -68,4 +69,23 @@ export async function validateBasePath(basePath, fs, dependencies = ['sap.fe.tem
68
69
  export function getErrorMessage(error) {
69
70
  return error instanceof Error ? error.message : String(error);
70
71
  }
72
+ /**
73
+ * Validates that a folder path is relative and does not contain path traversal sequences.
74
+ * This prevents security issues where malicious paths like '../../../etc' could write files outside the project.
75
+ *
76
+ * @param {string} folder - the folder path to validate
77
+ * @param {string} [paramName] - optional parameter name for error messages
78
+ * @throws {Error} if the path is absolute or contains '..'
79
+ */
80
+ export function validateRelativePath(folder, paramName = 'Path') {
81
+ // Check for absolute paths
82
+ if (isAbsolute(folder)) {
83
+ throw new Error(`${paramName} must not be an absolute path, got: ${folder}`);
84
+ }
85
+ // Check for path traversal attempts by examining path segments
86
+ const segments = folder.split(/[/\\]/);
87
+ if (segments.includes('..')) {
88
+ throw new Error(`${paramName} must not contain '..' (path traversal), got: ${folder}`);
89
+ }
90
+ }
71
91
  //# sourceMappingURL=validate.js.map
@@ -0,0 +1,17 @@
1
+ import type { Editor } from 'mem-fs-editor';
2
+ import type { Fragment } from './types.js';
3
+ /**
4
+ * Generate a standalone fragment file with default content.
5
+ * Does NOT modify manifest.json - fragment-only generation.
6
+ *
7
+ * @param {string} basePath - the base path
8
+ * @param {Fragment} fragment - fragment generation configuration
9
+ * @param {Editor} [fs] - the mem-fs editor instance
10
+ * @returns {Promise<Editor>} the updated mem-fs editor instance
11
+ * @throws {Error} if the project folder is invalid or manifest.json cannot be found
12
+ * @throws {Error} if sap.fe.templates is missing from manifest.json dependencies (only FPM projects are supported)
13
+ * @throws {Error} if fragment.folder is an absolute path or contains path traversal sequences (..)
14
+ * @throws {Error} if fragment.name is an absolute path or contains path traversal sequences (..)
15
+ */
16
+ export declare function generateFragment(basePath: string, fragment: Fragment, fs?: Editor): Promise<Editor>;
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,41 @@
1
+ import { create as createStorage } from 'mem-fs';
2
+ import { create } from 'mem-fs-editor';
3
+ import { join, dirname } from 'node:path';
4
+ import { validateBasePath, validateRelativePath } from '../common/validate.js';
5
+ import { getDefaultFragmentContent } from '../common/defaults.js';
6
+ import { copyTpl, createIdGenerator } from '../common/file.js';
7
+ import { getTemplatePath } from '../templates.js';
8
+ import { getManifest } from '../common/utils.js';
9
+ /**
10
+ * Generate a standalone fragment file with default content.
11
+ * Does NOT modify manifest.json - fragment-only generation.
12
+ *
13
+ * @param {string} basePath - the base path
14
+ * @param {Fragment} fragment - fragment generation configuration
15
+ * @param {Editor} [fs] - the mem-fs editor instance
16
+ * @returns {Promise<Editor>} the updated mem-fs editor instance
17
+ * @throws {Error} if the project folder is invalid or manifest.json cannot be found
18
+ * @throws {Error} if sap.fe.templates is missing from manifest.json dependencies (only FPM projects are supported)
19
+ * @throws {Error} if fragment.folder is an absolute path or contains path traversal sequences (..)
20
+ * @throws {Error} if fragment.name is an absolute path or contains path traversal sequences (..)
21
+ */
22
+ export async function generateFragment(basePath, fragment, fs) {
23
+ fs ??= create(createStorage());
24
+ await validateBasePath(basePath, fs);
25
+ const fnGenerateId = await createIdGenerator({ basePath, fsEditor: fs });
26
+ const { path: manifestPath } = await getManifest(basePath, fs);
27
+ // Calculate path for fragment with validation
28
+ const folder = fragment.folder ?? 'ext/fragment';
29
+ validateRelativePath(folder, 'Fragment folder');
30
+ validateRelativePath(fragment.name, 'Fragment name');
31
+ const path = join(dirname(manifestPath), folder);
32
+ // Generate default content if not provided
33
+ const content = fragment.content ?? getDefaultFragmentContent('Sample Text', fnGenerateId);
34
+ // Create fragment file
35
+ const viewPath = join(path, `${fragment.name}.fragment.xml`);
36
+ if (!fs.exists(viewPath)) {
37
+ copyTpl(fs, getTemplatePath('common/Fragment.xml'), viewPath, { content }, fnGenerateId);
38
+ }
39
+ return fs;
40
+ }
41
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Fragment generation configuration.
3
+ */
4
+ export interface Fragment {
5
+ /**
6
+ * Fragment name (without .fragment.xml extension).
7
+ */
8
+ name: string;
9
+ /**
10
+ * Optional folder path relative to webapp directory. Defaults to 'ext/fragment'.
11
+ */
12
+ folder?: string;
13
+ /**
14
+ * Optional XML content for the fragment. If not provided, a default fragment will be generated.
15
+ */
16
+ content?: string;
17
+ }
18
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
package/dist/index.d.ts CHANGED
@@ -8,6 +8,8 @@ export { TargetControl as ActionMenuTargetControl } from './action-menu/types.js
8
8
  export { generateActionMenu } from './action-menu/index.js';
9
9
  export type { CustomTableColumn } from './column/types.js';
10
10
  export { generateCustomColumn } from './column/index.js';
11
+ export type { Fragment } from './fragment/types.js';
12
+ export { generateFragment } from './fragment/index.js';
11
13
  export type { CustomHeaderSection, CustomSection, CustomSubSection } from './section/types.js';
12
14
  export { RequestGroupId, DesignTime } from './section/types.js';
13
15
  export { generateCustomSection, generateCustomSubSection, generateCustomHeaderSection } from './section/index.js';
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ export { generateCustomAction } from './action/index.js';
4
4
  export { TargetControl as ActionMenuTargetControl } from './action-menu/types.js';
5
5
  export { generateActionMenu } from './action-menu/index.js';
6
6
  export { generateCustomColumn } from './column/index.js';
7
+ export { generateFragment } from './fragment/index.js';
7
8
  export { RequestGroupId, DesignTime } from './section/types.js';
8
9
  export { generateCustomSection, generateCustomSubSection, generateCustomHeaderSection } from './section/index.js';
9
10
  export { generateCustomFilter } from './filter/index.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sap-ux/fe-fpm-writer",
3
3
  "description": "SAP Fiori elements flexible programming model writer",
4
- "version": "1.1.6",
4
+ "version": "1.2.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/SAP/open-ux-tools.git",
@@ -31,10 +31,10 @@
31
31
  "semver": "7.8.4",
32
32
  "xml-formatter": "3.7.0",
33
33
  "xpath": "0.0.34",
34
- "@sap-ux/fiori-annotation-api": "1.0.14",
34
+ "@sap-ux/fiori-annotation-api": "1.0.15",
35
35
  "@sap-ux/i18n": "1.0.2",
36
36
  "@sap-ux/project-access": "2.1.6",
37
- "@sap-ux/logger": "1.0.2"
37
+ "@sap-ux/logger": "1.0.3"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@jest/globals": "30.4.1",
@@ -44,7 +44,7 @@
44
44
  "@types/mem-fs-editor": "7.0.1",
45
45
  "@types/semver": "7.7.1",
46
46
  "@types/vinyl": "2.0.12",
47
- "@sap-ux/ui-prompting": "1.0.6"
47
+ "@sap-ux/ui-prompting": "1.0.7"
48
48
  },
49
49
  "engines": {
50
50
  "node": ">=22.x"