Skip to content

Commit 2bd0f11

Browse files
authored
Merge pull request #23 from cloudgraphdev/beta
RELEASE 0.21.0
2 parents 887c514 + 26c0893 commit 2bd0f11

File tree

13 files changed

+164
-1079
lines changed

13 files changed

+164
-1079
lines changed

.github/workflows/publish.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,6 @@ jobs:
4141
# See https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#use-private-packages
4242
run: yarn install --frozen-lockfile --prefer-offline --ignore-scripts
4343

44-
- name: Install jq for tests
45-
run: node -r node-jq/scripts/install-binary.js
46-
4744
- name: Build
4845
run: yarn prepack
4946

CHANGELOG.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,51 @@
1+
# [0.21.0-beta.1](https://github.com/cloudgraphdev/sdk/compare/0.20.0...0.21.0-beta.1) (2022-07-06)
2+
3+
4+
### Bug Fixes
5+
6+
* adapt CLI to pass data instead of queries ([dbaa7ce](https://github.com/cloudgraphdev/sdk/commit/dbaa7ced67870863792cedaaf80b965c6d11cbf0))
7+
* remove node-jq implementation ([c614842](https://github.com/cloudgraphdev/sdk/commit/c6148429e056cd64cc3055f861b597e2dbcdbb09))
8+
* Rollback missing resources status feature ([5eb10e9](https://github.com/cloudgraphdev/sdk/commit/5eb10e9c610e92ec608acbac1fdefb4a84ea109c))
9+
10+
11+
### Features
12+
13+
* adapt CLI to pass data instead of queries ([1d73587](https://github.com/cloudgraphdev/sdk/commit/1d735872a6703e3880b26df8ee8d33982d56c3f1))
14+
* Added unit tests ([0d14267](https://github.com/cloudgraphdev/sdk/commit/0d14267c357307b8812d4d35faa2a50bc73437a7))
15+
* Extend rule engine to include missing checks when the resource does not exist ([79f8dd3](https://github.com/cloudgraphdev/sdk/commit/79f8dd3c9ffa149c431feb09f9b087a8b92e5250))
16+
* Move getLinkedData method to utils folder ([171d937](https://github.com/cloudgraphdev/sdk/commit/171d937ba7d4f1b6b9c0f878bd58288724ba138e))
17+
* remove unused storageEngine param ([a9a4ccb](https://github.com/cloudgraphdev/sdk/commit/a9a4ccb3613e7cf3dbc08a1e74bddef9d082fbc4))
18+
19+
# [0.21.0-alpha.3](https://github.com/cloudgraphdev/sdk/compare/0.21.0-alpha.2...0.21.0-alpha.3) (2022-07-06)
20+
21+
22+
### Bug Fixes
23+
24+
* remove node-jq implementation ([c614842](https://github.com/cloudgraphdev/sdk/commit/c6148429e056cd64cc3055f861b597e2dbcdbb09))
25+
* Rollback missing resources status feature ([5eb10e9](https://github.com/cloudgraphdev/sdk/commit/5eb10e9c610e92ec608acbac1fdefb4a84ea109c))
26+
27+
# [0.21.0-alpha.2](https://github.com/cloudgraphdev/sdk/compare/0.21.0-alpha.1...0.21.0-alpha.2) (2022-06-30)
28+
29+
30+
### Bug Fixes
31+
32+
* adapt CLI to pass data instead of queries ([dbaa7ce](https://github.com/cloudgraphdev/sdk/commit/dbaa7ced67870863792cedaaf80b965c6d11cbf0))
33+
34+
35+
### Features
36+
37+
* adapt CLI to pass data instead of queries ([1d73587](https://github.com/cloudgraphdev/sdk/commit/1d735872a6703e3880b26df8ee8d33982d56c3f1))
38+
* Move getLinkedData method to utils folder ([171d937](https://github.com/cloudgraphdev/sdk/commit/171d937ba7d4f1b6b9c0f878bd58288724ba138e))
39+
* remove unused storageEngine param ([a9a4ccb](https://github.com/cloudgraphdev/sdk/commit/a9a4ccb3613e7cf3dbc08a1e74bddef9d082fbc4))
40+
41+
# [0.21.0-alpha.1](https://github.com/cloudgraphdev/sdk/compare/0.20.0...0.21.0-alpha.1) (2022-06-29)
42+
43+
44+
### Features
45+
46+
* Added unit tests ([0d14267](https://github.com/cloudgraphdev/sdk/commit/0d14267c357307b8812d4d35faa2a50bc73437a7))
47+
* Extend rule engine to include missing checks when the resource does not exist ([79f8dd3](https://github.com/cloudgraphdev/sdk/commit/79f8dd3c9ffa149c431feb09f9b087a8b92e5250))
48+
149
# [0.20.0](https://github.com/cloudgraphdev/sdk/compare/0.19.0...0.20.0) (2022-06-09)
250

351

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@cloudgraph/sdk",
3-
"version": "0.20.0",
3+
"version": "0.21.0-beta.1",
44
"description": "SDK for cloud graph providers and cli",
55
"main": "dist/src/index.js",
66
"types": "dist/src/index.d.ts",
@@ -29,7 +29,6 @@
2929
"inquirer": "^8.1.2",
3030
"jsonpath": "^1.1.1",
3131
"lodash": "^4.17.21",
32-
"node-jq": "^2.3.0",
3332
"ora": "^5.4.1"
3433
},
3534
"devDependencies": {

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, this.provider.schemasMap)
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: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import lodash from 'lodash'
2-
import * as jqNode from 'node-jq'
2+
33
import {
44
Condition,
55
JsonRule,
@@ -20,9 +20,12 @@ export default class JsonEvaluator implements RuleEvaluator<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
@@ -119,10 +122,9 @@ export default class JsonEvaluator implements RuleEvaluator<JsonRule> {
119122
_data: _ResourceData
120123
): Promise<number | boolean> {
121124
const condition = { ..._condition }
122-
const { path, value, jq: jqQuery } = condition
125+
const { path, value } = condition
123126
delete condition.path
124127
delete condition.value
125-
delete condition.jq
126128
// remaining field should be the op name
127129
const op = Object.keys(condition)[0] //
128130
const operator = this.operators[op]
@@ -155,24 +157,6 @@ export default class JsonEvaluator implements RuleEvaluator<JsonRule> {
155157
firstArg = value
156158
}
157159

158-
if (firstArg && jqQuery) {
159-
firstArg = await this.runJq(firstArg, jqQuery)
160-
lodash.set(data.data, data.elementPath, firstArg)
161-
}
162-
163160
return operator(firstArg, otherArgs, data)
164161
}
165-
166-
async runJq(data: unknown, jqQuery: string): Promise<unknown> {
167-
try {
168-
const json = (await jqNode.run(jqQuery, data, {
169-
input: 'json',
170-
output: 'json',
171-
})) as unknown
172-
173-
return json || data
174-
} catch (e) {
175-
return data
176-
}
177-
}
178162
}

src/rules-engine/index.ts

Lines changed: 6 additions & 4 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]
@@ -91,16 +93,16 @@ export default class RulesProvider implements Engine {
9193
const resourcePaths = data ? jsonpath.nodes(data, rule.resource) : []
9294
const evaluator = this.getRuleEvaluator(rule)
9395

96+
if (!evaluator) {
97+
return []
98+
}
99+
94100
if (isEmpty(data) && evaluator instanceof ManualEvaluator) {
95101
// Returned Manual Rule Response
96102
res.push(await evaluator.evaluateSingleResource(rule))
97103
return res
98104
}
99105

100-
if (!evaluator) {
101-
return []
102-
}
103-
104106
// @NOTE: here we can evaluate things such as Data being empty (may imply rule to pass)
105107
// or if we have no resources, or none pass, we can decide on that rule (+ some rule field)
106108
for (let i = 0; i < resourcePaths.length; i++) {

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 & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ export type ResourceData = {
99
export type Condition = {
1010
path?: string
1111
value?: string | number | Condition | (string | number)[]
12-
jq?: string
1312
[operationId: string]: any
1413
}
1514

@@ -65,6 +64,7 @@ export interface RuleFinding extends Finding {
6564

6665
export interface JsonRule extends Rule {
6766
conditions: Condition
67+
exclude?: Condition
6868
}
6969

7070
export interface JsRule extends Rule {

src/utils/data.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { ProviderData, SchemaMap } from '../types'
2+
3+
const getLinkedData = (providerData: ProviderData, schemasMap?: SchemaMap): 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+
// eslint-disable-next-line no-underscore-dangle
46+
const inverseConnField = schemasMap && schemasMap[entity.__typename] || 'account' // @TODO: account doesn't have a name
47+
if (!targetEntity[inverseConnField]) {
48+
targetEntity[inverseConnField] = []
49+
}
50+
targetEntity[inverseConnField].push(entity)
51+
} // else parent relation.. is not used atm
52+
}
53+
}
54+
55+
return linkedData
56+
}
57+
58+
export default getLinkedData

0 commit comments

Comments
 (0)