@tehw0lf/yaft 0.0.8 → 0.0.10
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/.github/workflows/build.yml +2 -0
- package/.github/workflows/security-scan.yml +79 -0
- package/jest.config.js +1 -1
- package/package.json +10 -10
- package/src/FeatureToggle.ts +4 -3
- package/src/examples/ApiServiceBooleanProvider.ts +1 -1
- package/src/test/decorator-behavior.spec.ts +6 -6
- package/src/test/error-handling.spec.ts +5 -2
- package/tsconfig.json +4 -1
|
@@ -17,6 +17,7 @@ jobs:
|
|
|
17
17
|
packages: write
|
|
18
18
|
id-token: write
|
|
19
19
|
security-events: write
|
|
20
|
+
pull-requests: write
|
|
20
21
|
with:
|
|
21
22
|
tool: "npm"
|
|
22
23
|
test: "run test"
|
|
@@ -24,3 +25,4 @@ jobs:
|
|
|
24
25
|
build_main: "run build"
|
|
25
26
|
artifact_path: "dist"
|
|
26
27
|
library_path: "dist"
|
|
28
|
+
npm_audit_omit_dev: true
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Security Monitoring Workflow
|
|
2
|
+
#
|
|
3
|
+
# This workflow performs security scanning using only the security-specific
|
|
4
|
+
# reusable workflows, without building or publishing.
|
|
5
|
+
#
|
|
6
|
+
# Schedule: Runs daily at 2:00 AM UTC to catch newly disclosed vulnerabilities
|
|
7
|
+
# Manual Trigger: Can be triggered via GitHub Actions UI or gh CLI
|
|
8
|
+
#
|
|
9
|
+
# Security Checks:
|
|
10
|
+
# - Source code scanning: Semgrep (OWASP Top 10, security-audit, CI), npm audit
|
|
11
|
+
# - Artifact scanning: Trivy on dist/ artifacts (requires prior build)
|
|
12
|
+
#
|
|
13
|
+
name: Security Monitoring
|
|
14
|
+
|
|
15
|
+
on:
|
|
16
|
+
schedule:
|
|
17
|
+
# Run daily at 2:00 AM UTC
|
|
18
|
+
- cron: "0 2 * * *"
|
|
19
|
+
workflow_dispatch:
|
|
20
|
+
# Allow manual triggering
|
|
21
|
+
|
|
22
|
+
permissions:
|
|
23
|
+
contents: read
|
|
24
|
+
security-events: write
|
|
25
|
+
actions: read
|
|
26
|
+
pull-requests: write
|
|
27
|
+
|
|
28
|
+
jobs:
|
|
29
|
+
# Scan source code for security vulnerabilities
|
|
30
|
+
scan_source:
|
|
31
|
+
name: Scan Source Code
|
|
32
|
+
uses: tehw0lf/workflows/.github/workflows/security-scan-source.yml@main
|
|
33
|
+
permissions:
|
|
34
|
+
contents: write
|
|
35
|
+
security-events: write
|
|
36
|
+
actions: read
|
|
37
|
+
pull-requests: write
|
|
38
|
+
with:
|
|
39
|
+
tool: "npm"
|
|
40
|
+
semgrep_rules: "auto p/security-audit p/owasp-top-ten p/ci p/nodejs p/typescript"
|
|
41
|
+
npm_audit_omit_dev: true
|
|
42
|
+
npm_audit_severity_threshold: "high"
|
|
43
|
+
|
|
44
|
+
# Download artifacts from latest CI build
|
|
45
|
+
# Note: Reuses artifacts from the main CI workflow instead of rebuilding
|
|
46
|
+
download_artifacts:
|
|
47
|
+
name: Download Build Artifacts
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
|
|
50
|
+
steps:
|
|
51
|
+
- name: Download latest dist artifacts from CI
|
|
52
|
+
uses: dawidd6/action-download-artifact@v6
|
|
53
|
+
with:
|
|
54
|
+
workflow: build.yml
|
|
55
|
+
name: dist
|
|
56
|
+
path: dist/
|
|
57
|
+
if_no_artifact_found: warn
|
|
58
|
+
|
|
59
|
+
- name: Upload artifacts for scanning
|
|
60
|
+
if: hashFiles('dist/**/*') != ''
|
|
61
|
+
uses: actions/upload-artifact@v4
|
|
62
|
+
with:
|
|
63
|
+
name: build
|
|
64
|
+
path: dist/
|
|
65
|
+
retention-days: 1
|
|
66
|
+
|
|
67
|
+
# Scan built artifacts for vulnerabilities
|
|
68
|
+
scan_artifacts:
|
|
69
|
+
name: Scan Artifacts
|
|
70
|
+
needs: download_artifacts
|
|
71
|
+
if: needs.download_artifacts.result == 'success'
|
|
72
|
+
uses: tehw0lf/workflows/.github/workflows/security-scan-artifacts.yml@main
|
|
73
|
+
permissions:
|
|
74
|
+
contents: read
|
|
75
|
+
security-events: write
|
|
76
|
+
actions: read
|
|
77
|
+
with:
|
|
78
|
+
tool: "npm"
|
|
79
|
+
artifact_path: "dist"
|
package/jest.config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tehw0lf/yaft",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "YaFT - Feature Toggles using Go&PostgreSQL or any source!",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"build": "tsc &&
|
|
7
|
+
"build": "tsc && cp package.json README.md logo.svg dist/yaft/ && npm pkg delete devDependencies --prefix dist/yaft",
|
|
8
8
|
"test": "npx jest"
|
|
9
9
|
},
|
|
10
10
|
"repository": {
|
|
@@ -24,15 +24,15 @@
|
|
|
24
24
|
},
|
|
25
25
|
"homepage": "https://github.com/tehw0lf/yaft-ts#readme",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"reflect-metadata": "
|
|
27
|
+
"reflect-metadata": "0.2.2"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@types/jest": "
|
|
31
|
-
"@types/node": "
|
|
32
|
-
"axios": "
|
|
33
|
-
"jest": "
|
|
34
|
-
"ts-jest": "
|
|
35
|
-
"ts-node": "
|
|
36
|
-
"typescript": "
|
|
30
|
+
"@types/jest": "30.0.0",
|
|
31
|
+
"@types/node": "24.1.0",
|
|
32
|
+
"axios": "1.18.0",
|
|
33
|
+
"jest": "30.4.2",
|
|
34
|
+
"ts-jest": "29.4.11",
|
|
35
|
+
"ts-node": "10.9.2",
|
|
36
|
+
"typescript": "6.0.3"
|
|
37
37
|
}
|
|
38
38
|
}
|
package/src/FeatureToggle.ts
CHANGED
|
@@ -25,7 +25,6 @@ export function FeatureToggle(key: string, fallback?: any) {
|
|
|
25
25
|
if (!FeatureToggleBase.featureProvider) {
|
|
26
26
|
throw new Error("FeatureToggleProvider not set");
|
|
27
27
|
}
|
|
28
|
-
const isEnabled = FeatureToggleBase.featureProvider.isEnabled(key);
|
|
29
28
|
return (
|
|
30
29
|
target: any,
|
|
31
30
|
propertyKey?: string,
|
|
@@ -36,12 +35,13 @@ export function FeatureToggle(key: string, fallback?: any) {
|
|
|
36
35
|
const originalMethod = descriptor.value;
|
|
37
36
|
|
|
38
37
|
descriptor.value = function (...args: any[]) {
|
|
38
|
+
const isEnabled = FeatureToggleBase.featureProvider.isEnabled(key);
|
|
39
39
|
if (isEnabled) {
|
|
40
40
|
return originalMethod.apply(this, args);
|
|
41
41
|
} else {
|
|
42
42
|
return fallback !== undefined
|
|
43
|
-
? fallback.apply(this, args)
|
|
44
|
-
: (() => {}).apply(this, args);
|
|
43
|
+
? fallback.apply(this, args as [])
|
|
44
|
+
: (() => {}).apply(this, args as []);
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
47
|
return descriptor;
|
|
@@ -50,6 +50,7 @@ export function FeatureToggle(key: string, fallback?: any) {
|
|
|
50
50
|
const originalConstructor = target;
|
|
51
51
|
let newConstructor: any;
|
|
52
52
|
|
|
53
|
+
const isEnabled = FeatureToggleBase.featureProvider.isEnabled(key);
|
|
53
54
|
if (isEnabled) {
|
|
54
55
|
newConstructor = originalConstructor;
|
|
55
56
|
} else {
|
|
@@ -5,7 +5,7 @@ import { FeatureProvider } from "../FeatureToggle";
|
|
|
5
5
|
export class ApiServiceBooleanProvider implements FeatureProvider<boolean> {
|
|
6
6
|
apiUrl: string;
|
|
7
7
|
baseUUID: string;
|
|
8
|
-
data: Record<string, boolean
|
|
8
|
+
data: Record<string, boolean> = {};
|
|
9
9
|
collectionHash = "";
|
|
10
10
|
|
|
11
11
|
constructor(apiUrl: string, baseUUID: string) {
|
|
@@ -288,12 +288,12 @@ describe('FeatureToggle Decorator Behavior', () => {
|
|
|
288
288
|
throw new Error('Provider error');
|
|
289
289
|
});
|
|
290
290
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
291
|
+
class TestClass {
|
|
292
|
+
@FeatureToggle('errorFeature')
|
|
293
|
+
testMethod() {}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
expect(() => new TestClass().testMethod()).toThrow('Provider error');
|
|
297
297
|
});
|
|
298
298
|
|
|
299
299
|
it('should handle undefined/null feature keys', () => {
|
|
@@ -317,8 +317,11 @@ describe('Error Handling and Edge Cases', () => {
|
|
|
317
317
|
}
|
|
318
318
|
|
|
319
319
|
const instance = new TestClass();
|
|
320
|
-
|
|
320
|
+
|
|
321
321
|
expect(instance.method1()).toBe(1);
|
|
322
|
+
expect(instance.method2()).toBe(2);
|
|
323
|
+
expect(instance.method3()).toBe(3);
|
|
324
|
+
expect(instance.method4()).toBe(4);
|
|
322
325
|
expect(instance.method5()).toBe(5);
|
|
323
326
|
expect(mockProvider.isEnabled).toHaveBeenCalledTimes(5);
|
|
324
327
|
});
|
|
@@ -364,7 +367,7 @@ describe('Error Handling and Edge Cases', () => {
|
|
|
364
367
|
'objectFeature': { enabled: true },
|
|
365
368
|
'nullFeature': null,
|
|
366
369
|
'undefinedFeature': undefined
|
|
367
|
-
},
|
|
370
|
+
} as Record<string, unknown>,
|
|
368
371
|
getConfig: jest.fn(),
|
|
369
372
|
isEnabled: (key: string) => {
|
|
370
373
|
const value = mixedProvider.data[key];
|
package/tsconfig.json
CHANGED
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
"target": "ES2022",
|
|
5
5
|
"experimentalDecorators": true,
|
|
6
6
|
"declaration": true,
|
|
7
|
-
"
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"outDir": "./dist/yaft",
|
|
9
|
+
"types": ["node"],
|
|
10
|
+
"lib": ["ES2022"]
|
|
8
11
|
},
|
|
9
12
|
"include": ["src/**/*.ts"],
|
|
10
13
|
"exclude": ["**/*.spec.ts", "**/*.test.ts", "**/test/**/*"]
|