Skip to content

Commit d4acc6e

Browse files
authored
Merge pull request #18 from cloudgraphdev/feature/EP-1988-main
feat: adapt CLI to pass data instead of queries
2 parents 6235d34 + 171d937 commit d4acc6e

File tree

6 files changed

+80
-10
lines changed

6 files changed

+80
-10
lines changed

src/plugins/policyPack/index.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { Result, Rule, Severity } from '../../rules-engine/types'
1515
import Plugin, { ConfiguredPlugin, PluginManager } from '../types'
1616
import DgraphDataProcessor from '../../rules-engine/data-processors/dgraph-data-processor'
1717
import DataProcessor from '../../rules-engine/data-processors/data-processor'
18+
import getLinkedData from '../../utils/data'
1819

1920
export default class PolicyPackPlugin extends Plugin {
2021
constructor({
@@ -137,13 +138,13 @@ export default class PolicyPackPlugin extends Plugin {
137138
}
138139

139140
private async executeRule({
141+
data,
140142
rules,
141143
policyPack,
142-
storageEngine,
143144
}: {
145+
data: any,
144146
rules: Rule[]
145147
policyPack: string
146-
storageEngine: StorageEngine
147148
}): Promise<RuleFinding[]> {
148149
const findings: RuleFinding[] = []
149150

@@ -159,18 +160,16 @@ export default class PolicyPackPlugin extends Plugin {
159160

160161
findings.push(
161162
...(await this.executeRule({
163+
data,
162164
rules: subRules,
163165
policyPack,
164-
storageEngine,
165166
}))
166167
)
167168
} else {
168-
const { data } = rule.gql
169-
? await storageEngine.query(rule.gql)
170-
: { data: undefined }
169+
const ruleData = rule.gql ? data : undefined
171170
const results = (await this.policyPacksPlugins[
172171
policyPack
173-
]?.engine?.processRule(rule, data)) as RuleFinding[]
172+
]?.engine?.processRule(rule, ruleData)) as RuleFinding[]
174173

175174
findings.push(...results)
176175
}
@@ -277,10 +276,12 @@ export default class PolicyPackPlugin extends Plugin {
277276
async execute({
278277
storageRunning,
279278
storageEngine,
279+
providerData,
280280
processConnectionsBetweenEntities,
281281
}: {
282282
storageRunning: boolean
283283
storageEngine: StorageEngine
284+
providerData: ProviderData
284285
processConnectionsBetweenEntities: (props: {
285286
provider?: string
286287
providerData: ProviderData
@@ -314,10 +315,13 @@ export default class PolicyPackPlugin extends Plugin {
314315
mergeSchemas(currentSchema, findingsSchema),
315316
])
316317

318+
// Format metadata and link connections
319+
const linkedData = getLinkedData(providerData)
320+
317321
const findings = await this.executeRule({
322+
data: linkedData,
318323
rules,
319324
policyPack,
320-
storageEngine,
321325
})
322326

323327
// Prepare mutations

src/rules-engine/evaluators/json-evaluator.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,12 @@ export default class JsonEvaluator extends BaseEvaluator<JsonRule> {
2020
}
2121

2222
async evaluateSingleResource(
23-
{ id, conditions, severity }: JsonRule,
23+
{ id, conditions, severity, exclude }: JsonRule,
2424
data: ResourceData
2525
): Promise<RuleFinding> {
26+
if (exclude && (await this.evaluateCondition(exclude, data))) {
27+
return
28+
}
2629
const result = (await this.evaluateCondition(conditions, data))
2730
? RuleResult.MATCHES
2831
: RuleResult.DOESNT_MATCH
@@ -157,6 +160,7 @@ export default class JsonEvaluator extends BaseEvaluator<JsonRule> {
157160

158161
if (firstArg && jqQuery) {
159162
firstArg = await this.runJq(firstArg, jqQuery)
163+
data.data = lodash.cloneDeep(data.data)
160164
lodash.set(data.data, data.elementPath, firstArg)
161165
}
162166

src/rules-engine/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ export default class RulesProvider implements Engine {
5151
): Promise<RuleFinding> => {
5252
const finding = await evaluator.evaluateSingleResource(rule, data)
5353

54+
if (!finding) return
55+
5456
// Inject extra fields
5557
for (const field of this.dataProcessor.extraFields) {
5658
finding[field] = data.resource[field]

src/rules-engine/operators/common.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ export default {
1313
return shouldBeEmpty ? hasKeys === 0 : hasKeys > 0
1414
}
1515

16-
return false
16+
return (
17+
(data === null || data === undefined || data === '') === shouldBeEmpty
18+
)
1719
},
1820
}

src/rules-engine/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export interface RuleFinding extends Finding {
6565

6666
export interface JsonRule extends Rule {
6767
conditions: Condition
68+
exclude?: Condition
6869
}
6970

7071
export interface JsRule extends Rule {

src/utils/data.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { ProviderData } from '../types'
2+
3+
const getLinkedData = (providerData: ProviderData): any => {
4+
const linkedData = {}
5+
const allEntities = providerData?.entities || []
6+
const allConnections = providerData?.connections || {}
7+
const entitiesById: { [key: string]: any } = {}
8+
9+
for (const entity of allEntities) {
10+
// AddawsEc2Input! => queryawsEc2
11+
const mutationName = /(?<=\[)(.*?)(?=\])/
12+
.exec(entity.mutation as any)[0]
13+
.replace('Add', 'query')
14+
.replace('Input!', '')
15+
16+
linkedData[mutationName] = entity.data
17+
18+
for (const entityData of entity.data) {
19+
entitiesById[entityData.id] = entityData
20+
// eslint-disable-next-line no-underscore-dangle
21+
entityData.__typename = mutationName.replace('query', '') // or entity.name?
22+
}
23+
}
24+
25+
// connect data on a second pass
26+
for (const entityId of Object.keys(allConnections)) {
27+
const entityConnections = allConnections[entityId]
28+
const entity = entitiesById[entityId]
29+
if (!entity) {
30+
// eslint-disable-next-line no-continue
31+
continue
32+
}
33+
for (const conn of entityConnections) {
34+
const targetEntity = entitiesById[conn.id]
35+
if (!targetEntity) {
36+
// eslint-disable-next-line no-continue
37+
continue
38+
}
39+
if (conn.relation === 'child') {
40+
if (!entity[conn.field]) {
41+
entity[conn.field] = []
42+
}
43+
entity[conn.field].push(targetEntity)
44+
// inverse relation
45+
// const inverseConnField = this.schemasMap[entity.__typename] || 'account' // @TODO: account doesn't have a name
46+
// if (!targetEntity[inverseConnField]) {
47+
// targetEntity[inverseConnField] = []
48+
// }
49+
// targetEntity[inverseConnField].push(entity)
50+
} // else parent relation.. is not used atm
51+
}
52+
}
53+
54+
return linkedData
55+
}
56+
57+
export default getLinkedData

0 commit comments

Comments
 (0)