@@ -4,13 +4,21 @@ import { responseExceptionSilent } from '../exceptions/index.js';
44import { auth , gitopsAuth } from '../middleware/auth.js' ;
55import { validate } from '../middleware/validators.js' ;
66import { featureFlag , validateChanges } from '../middleware/gitops.js' ;
7+ import { notifyGitopsSubscription } from '../external/switcher-api-facade.js' ;
78import * as Service from '../services/gitops/index.js' ;
9+ import { verifyOwnership } from '../helpers/index.js' ;
10+ import { ActionTypes , RouterTypes } from '../models/permission.js' ;
811
912const router = new express . Router ( ) ;
10- const regex = new RegExp ( / ^ \d + [ s m h ] $ / ) ;
13+
14+ // Allow only values like 1s, 1m, 1h
15+ const regexWindowInterval = new RegExp ( / ^ \d + [ s m h ] $ / ) ;
16+
17+ // Allow slash, alphanumeric, hyphen, underscore, dot only
18+ const regexPath = new RegExp ( / ^ [ a - z A - Z 0 - 9 / _ \- . ] + $ / ) ;
1119
1220const windowValidation = ( value ) => {
13- if ( ! regex . test ( value ) ) {
21+ if ( ! regexWindowInterval . test ( value ) ) {
1422 throw new Error ( 'Invalid window value' ) ;
1523 }
1624
@@ -25,11 +33,23 @@ const windowValidation = (value) => {
2533 return true ;
2634} ;
2735
36+ const pathValidation = ( value ) => {
37+ if ( value . startsWith ( '/' ) || value . endsWith ( '/' ) || value . includes ( '//' ) ) {
38+ throw new Error ( 'Invalid path value - cannot start or end with / or contain //' ) ;
39+ }
40+
41+ if ( ! regexPath . test ( value ) ) {
42+ throw new Error ( 'Invalid path value - only alphanumeric characters and / are allowed' ) ;
43+ }
44+
45+ return true ;
46+ } ;
47+
2848const accountValidators = [
2949 body ( 'token' ) . isString ( ) . optional ( ) ,
3050 body ( 'repository' ) . isURL ( ) . withMessage ( 'Invalid repository URL' ) ,
3151 body ( 'branch' ) . isString ( ) . withMessage ( 'Invalid branch name' ) ,
32- body ( 'path' ) . isString ( ) . optional ( ) . withMessage ( 'Invalid path' ) ,
52+ body ( 'path' ) . isString ( ) . optional ( ) . custom ( pathValidation ) ,
3353 body ( 'environment' ) . isString ( ) . withMessage ( 'Invalid environment name' ) ,
3454 body ( 'domain.id' ) . isMongoId ( ) . withMessage ( 'Invalid domain ID' ) ,
3555 body ( 'domain.name' ) . isString ( ) . withMessage ( 'Invalid domain name' ) ,
@@ -38,6 +58,16 @@ const accountValidators = [
3858 body ( 'settings.forceprune' ) . isBoolean ( ) . withMessage ( 'Invalid forceprune flag' ) ,
3959] ;
4060
61+ const verifyOwnershipMiddleware = async ( req , res , next ) => {
62+ try {
63+ const domainId = req . body ?. domain . id || req . params . domain ;
64+ await verifyOwnership ( req . admin , domainId , domainId , ActionTypes . UPDATE , RouterTypes . ADMIN ) ;
65+ next ( ) ;
66+ } catch ( e ) {
67+ responseExceptionSilent ( res , e , 403 , 'Permission denied' ) ;
68+ }
69+ } ;
70+
4171router . post ( '/gitops/v1/push' , gitopsAuth , featureFlag , [
4272 body ( 'environment' ) . isString ( ) ,
4373 body ( 'changes' ) . isArray ( ) ,
@@ -58,9 +88,11 @@ router.post('/gitops/v1/push', gitopsAuth, featureFlag, [
5888} ) ;
5989
6090router . post ( '/gitops/v1/account/subscribe' , auth , accountValidators , validate ,
61- featureFlag , async ( req , res ) => {
91+ featureFlag , verifyOwnershipMiddleware , async ( req , res ) => {
6292 try {
6393 const account = await Service . subscribeAccount ( req . body ) ;
94+ notifyGitopsSubscription ( 'subscribe' ) ;
95+
6496 res . status ( 201 ) . send ( account ) ;
6597 } catch ( e ) {
6698 responseExceptionSilent ( res , e , 500 , 'Account subscription failed' ) ;
@@ -70,17 +102,19 @@ router.post('/gitops/v1/account/subscribe', auth, accountValidators, validate,
70102router . post ( '/gitops/v1/account/unsubscribe' , auth , [
71103 body ( 'environment' ) . isString ( ) ,
72104 body ( 'domain.id' ) . isMongoId ( ) . withMessage ( 'Invalid domain ID' ) ,
73- ] , validate , featureFlag , async ( req , res ) => {
105+ ] , validate , featureFlag , verifyOwnershipMiddleware , async ( req , res ) => {
74106 try {
75107 await Service . unsubscribeAccount ( req . body ) ;
108+ notifyGitopsSubscription ( 'unsubscribe' ) ;
109+
76110 res . status ( 200 ) . send ( ) ;
77111 } catch ( e ) {
78112 responseExceptionSilent ( res , e , 500 , 'Account unsubscription failed' ) ;
79113 }
80114} ) ;
81115
82116router . put ( '/gitops/v1/account' , auth , accountValidators , validate ,
83- featureFlag , async ( req , res ) => {
117+ featureFlag , verifyOwnershipMiddleware , async ( req , res ) => {
84118 try {
85119 const account = await Service . updateAccount ( req . body ) ;
86120 res . status ( 200 ) . send ( account ) ;
@@ -93,10 +127,10 @@ router.put('/gitops/v1/account/tokens', auth, [
93127 body ( 'token' ) . isString ( ) ,
94128 body ( 'environments' ) . isArray ( ) ,
95129 body ( 'domain.id' ) . isMongoId ( ) . withMessage ( 'Invalid domain ID' ) ,
96- ] , validate , featureFlag , async ( req , res ) => {
130+ ] , validate , featureFlag , verifyOwnershipMiddleware , async ( req , res ) => {
97131 try {
98- const account = await Service . updateAccountTokens ( req . body ) ;
99- res . status ( 200 ) . send ( account ) ;
132+ const result = await Service . updateAccountTokens ( req . body ) ;
133+ res . status ( 200 ) . send ( result ) ;
100134 } catch ( e ) {
101135 responseExceptionSilent ( res , e , 500 , 'Account token update failed' ) ;
102136 }
@@ -105,7 +139,7 @@ router.put('/gitops/v1/account/tokens', auth, [
105139router . put ( '/gitops/v1/account/forcesync' , auth , [
106140 body ( 'environment' ) . isString ( ) ,
107141 body ( 'domain.id' ) . isMongoId ( ) . withMessage ( 'Invalid domain ID' ) ,
108- ] , validate , featureFlag , async ( req , res ) => {
142+ ] , validate , featureFlag , verifyOwnershipMiddleware , async ( req , res ) => {
109143 try {
110144 const account = await Service . forceSyncAccount ( req . body ) ;
111145 res . status ( 200 ) . send ( account ) ;
@@ -117,7 +151,7 @@ router.put('/gitops/v1/account/forcesync', auth, [
117151router . get ( '/gitops/v1/account/:domain' , auth , [
118152 check ( 'domain' ) . isMongoId ( ) . withMessage ( 'Invalid domain ID' ) ,
119153 check ( 'environment' ) . optional ( ) . isString ( ) ,
120- ] , validate , featureFlag , async ( req , res ) => {
154+ ] , validate , featureFlag , verifyOwnershipMiddleware , async ( req , res ) => {
121155 try {
122156 const accounts = await Service . fetchAccounts ( req . params . domain , req . query . environment || null ) ;
123157 res . status ( 200 ) . send ( accounts ) ;
0 commit comments