Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/checker/common/full-callgraph-file-entrypoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ function makeFullCallGraph(analyzer: any): void {
funcSymbolAny2.fdef.type === 'FunctionDefinition'
) {
alreadyCheckList.push(funcSymbolAny2)
const argValues: any[] = []
const argValues: Record<number | string, any> = []
analyzer.executeCall(
funcSymbolAny2.fdef,
funcSymbolAny2,
Expand Down
4 changes: 2 additions & 2 deletions src/checker/common/ql-uast-convert/converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,9 @@ function calcEntryPointAndRun(options: any, fileManager: any, analyzer: any): vo
entryPoints.forEach((main: any) => {
const nd = AstUtilConverter.satisfy(main.ast, (n: any) => n?._meta?.isSource === true)
if (nd) {
const argValues: any[] = []
const argValues: Record<number | string, any> = []
for (const key in main?.ast?.parameters) {
argValues.push(analyzer.processInstruction(filescope, main.ast.parameters[key], state))
argValues[key] = analyzer.processInstruction(filescope, main.ast.parameters[key], state)
}
logger.info(`entryPoint ${main?.ast?.loc?.sourcefile}:${main.id}`)
analyzer.executeCall(main.ast, main, argValues, state, filescope)
Expand Down
6 changes: 3 additions & 3 deletions src/checker/common/rules-basic-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ function getRules(ruleConfigPath: string): any[] {
* @param fclos
* @param rule
*/
function prepareArgs(argvalues: any[], fclos: any, rule: Rule): any[] {
function prepareArgs(argvalues: Record<number | string, any>, fclos: any, rule: Rule): any[] {
let { args } = rule
let res = argvalues.concat()
let res = Object.values(argvalues).concat()
args = (args || []).map((item: string | number) => {
if (item !== '*') {
return parseInt(String(item))
Expand All @@ -58,7 +58,7 @@ function prepareArgs(argvalues: any[], fclos: any, rule: Rule): any[] {
})
if (!args.some((v: string | number) => v === '*')) {
args = args.filter((v: string | number) => typeof v === 'number')
res = argvalues.filter((value: any, index: number) => {
res = Object.values(argvalues).filter((value: any, index: number) => {
return (args as number[]).indexOf(index) !== -1
})
}
Expand Down
2 changes: 1 addition & 1 deletion src/checker/sanitizer/sanitizer-checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ class SanitizerChecker extends Checker {
node: any,
fclos: any,
ret: any,
argvalues: any[],
argvalues: Record<number | string, any>,
scope: any,
callstack: any
): void {
Expand Down
23 changes: 19 additions & 4 deletions src/checker/taint/common-kit/sink-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,22 @@ interface SinkRule {
* @param argvalues
* @returns {Array}
*/
function matchSinkAtFuncCall(node: any, fclos: any, sinks: SinkRule[], argvalues: any[]): SinkRule[] {
function matchSinkAtFuncCall(
node: any,
fclos: any,
sinks: SinkRule[],
argvalues: Record<number | string, any>
): SinkRule[] {
const callExpr = node.callee || node
const res: SinkRule[] = []
if (sinks && sinks.length > 0) {
for (const tspec of sinks) {
if (tspec.argNum !== undefined && tspec.argNum >= 0 && argvalues && tspec.argNum !== argvalues.length) {
if (
tspec.argNum !== undefined &&
tspec.argNum >= 0 &&
argvalues &&
tspec.argNum !== Object.keys(argvalues).length
) {
continue
}

Expand Down Expand Up @@ -56,7 +66,7 @@ function matchSinkAtFuncCallWithCalleeType(
fclos: any,
rules: SinkRule[],
scope: any,
argvalues: any[]
argvalues: Record<number | string, any>
): SinkRule[] {
const callExpr = node.callee || node
const res: SinkRule[] = []
Expand All @@ -68,7 +78,12 @@ function matchSinkAtFuncCallWithCalleeType(
return res
}
for (const tspec of rules) {
if (tspec.argNum !== undefined && tspec.argNum >= 0 && argvalues && tspec.argNum !== argvalues.length) {
if (
tspec.argNum !== undefined &&
tspec.argNum >= 0 &&
argvalues &&
tspec.argNum !== Object.keys(argvalues).length
) {
continue
}

Expand Down
2 changes: 1 addition & 1 deletion src/checker/taint/go/gRpc-entrypoint-collect-checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class GRpcEntrypointCollectChecker extends Checker {
const { fclos, argvalues } = info
if (config.entryPointMode === 'ONLY_CUSTOM') return // 不路由自采集
if (!(fclos._qid in registerServerPoints)) return // 处理Register_xxx_Server函数,即实现类注册点
if (!Array.isArray(argvalues) || argvalues.length < 1) return
if (!Array.isArray(argvalues) || Object.keys(argvalues).length < 1) return
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The Array.isArray(argvalues) check is too restrictive now that argvalues is a Record. If argvalues is passed as a plain object (as seen in the Python analyzer changes), this check will cause an early return and skip processing. You should check if it is truthy and has keys instead.

Suggested change
if (!Array.isArray(argvalues) || Object.keys(argvalues).length < 1) return
if (!argvalues || Object.keys(argvalues).length < 1) return

const serverName = registerServerPoints[fclos._qid]
const implServer = argvalues[1]
this.searchServiceEntryPoints(serverName, implServer, fclos, state, analyzer)
Expand Down
2 changes: 1 addition & 1 deletion src/checker/taint/go/gin-default-taint-checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ class GinDefaultTaintChecker extends TaintChecker {
!callExpNode.loc ||
!calleeObject ||
!argValues ||
argValues.length <= 0
Object.keys(argValues).length <= 0
)
return null
const routeFCloses = GinEntryPoint.collectRouteRegistry(callExpNode, calleeObject, argValues, scope)
Expand Down
2 changes: 1 addition & 1 deletion src/checker/taint/go/gin-taint-checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ class GinTaintChecker extends TaintChecker {
!callExpNode.loc ||
!calleeObject ||
!argValues ||
argValues.length <= 0
Object.keys(argValues).length <= 0
)
return null
const routeFCloses = GinEntryPoint.collectRouteRegistry(callExpNode, calleeObject, argValues, scope)
Expand Down
4 changes: 2 additions & 2 deletions src/checker/taint/go/sync-once-do-checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const syncOnceDoQid: string = 'sync.Once<instance>.Do'

interface TriggerInfo {
fclos: any
argvalues: any[]
argvalues: Record<number | string, any>
[key: string]: any
}

Expand Down Expand Up @@ -36,7 +36,7 @@ class syncOnceDoChecker extends CheckerSyncOnceDo {
const hash: string = JSON.stringify(node.loc)
if (done.has(hash)) return
done.add(hash)
if (argvalues.length !== 1 && argvalues[0].vtype !== 'fclos') return
if (Object.keys(argvalues).length !== 1 && argvalues[0].vtype !== 'fclos') return

const fDef = node.arguments[0]
const fClos = argvalues[0]
Expand Down
100 changes: 59 additions & 41 deletions src/engine/analyzer/common/analyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2123,7 +2123,8 @@ class Analyzer extends MemSpace {

// attach taint information if any
// var taint;
for (const arg of argvalues) {
for (const key in argvalues) {
const arg = argvalues[key]
if (arg) {
if (arg.hasTagRec) {
res.hasTagRec = true
Expand All @@ -2137,7 +2138,8 @@ class Analyzer extends MemSpace {
}

// e.g. XXInterface token = XXInterface(id) where id is ctor_init
for (const arg of argvalues) {
for (const key in argvalues) {
const arg = argvalues[key]
if (arg && arg.ctor_init && node.expression && node.expression.value) {
let top_scope = scope
while (top_scope.parent) {
Expand All @@ -2159,7 +2161,7 @@ class Analyzer extends MemSpace {
res = SymbolValue(res) // esp. for member getter function

// save pass-in arguments for later use
if (argvalues.length > 0) {
if (Object.keys(argvalues).length > 0) {
res.setMisc('pass-in', argvalues)
}
return res
Expand Down Expand Up @@ -2229,7 +2231,12 @@ class Analyzer extends MemSpace {
scope: any,
state: any
) {
if (!argvalues || argvalues.length < 2 || !targetIndex || targetIndex >= argvalues.length) {
if (
!argvalues ||
Object.keys(argvalues).length < 2 ||
!targetIndex ||
targetIndex >= Object.keys(argvalues).length
) {
return
}
const res = argvalues[targetIndex]
Expand Down Expand Up @@ -2464,14 +2471,18 @@ class Analyzer extends MemSpace {
if (param) {
paramLength = Array.isArray(param) ? param.length : param.parameters.length
}
if (paramLength === argvalues.length) {
if (paramLength === Object.keys(argvalues).length) {
let typeMatch = true
const literalTypeList = ['String', 'string', 'int', 'Integer', 'Double', 'double', 'float', 'Float']
for (let i = 0; i < paramLength; i++) {
let argv = argvalues[i]
if (!argv && param[i].id?.name) {
argv = argvalues[param[i].id.name]
}
if (
param[i].varType?.id?.name === argvalues[i].rtype?.definiteType?.name ||
argvalues[i].rtype?.definiteType?.name?.endsWith(`.${param[i].varType?.id?.name}`) ||
(argvalues[i].vtype === 'primitive' && literalTypeList.includes(param[i].varType?.id?.name))
param[i].varType?.id?.name === argv?.rtype?.definiteType?.name ||
argv?.rtype?.definiteType?.name?.endsWith(`.${param[i].varType?.id?.name}`) ||
(argv?.vtype === 'primitive' && literalTypeList.includes(param[i].varType?.id?.name))
) {
continue
}
Expand All @@ -2493,7 +2504,7 @@ class Analyzer extends MemSpace {
if (param) {
paramLength = Array.isArray(param) ? param.length : param.parameters.length
}
if (paramLength === argvalues.length) {
if (paramLength === Object.keys(argvalues).length) {
fclos = _.clone(fclos)
fclos.ast = fclos.fdef = fdecl = f // adjust to the right function definition
break
Expand Down Expand Up @@ -2557,8 +2568,14 @@ class Analyzer extends MemSpace {

// this.lastReturnValue = fclos.execute.call(this, fclos, argvalues, new_state, node, scope);
this.lastReturnValue = null
for (let i = 0; i < argvalues.length; i++) {
argvalues[i] = SourceLine.addSrcLineInfo(argvalues[i], node, node.loc && node.loc.sourcefile, 'CALL: ', fname)
for (const key in argvalues) {
argvalues[key] = SourceLine.addSrcLineInfo(
argvalues[key],
node,
node.loc && node.loc.sourcefile,
'CALL: ',
fname
)
}
return_value = fclos.execute.call(this, fclos, argvalues, new_state, node, scope)
} else {
Expand All @@ -2585,10 +2602,10 @@ class Analyzer extends MemSpace {
this.processInstruction(fscope, param, new_state)
})

const size = Math.min(argvalues.length, params.length)
const size = params.length
let hasVariadicElement = false
if (
argvalues.length > size &&
Object.keys(argvalues).length > size &&
((Config.language !== 'js' && Config.language !== 'javascript') ||
params[params.length - 1]?._meta.isRestElement)
) {
Expand All @@ -2600,27 +2617,27 @@ class Analyzer extends MemSpace {
const paramName = param.id?.name
if (i === size - 1 && hasVariadicElement) {
// variadic parameter processing
const rest_argvalues = argvalues.slice(i)
const rest_val: any = {}
rest_argvalues.forEach((element: any, index: any) => {
rest_val[index.toString()] = element
})
for (let j = 0; argvalues[i + j]; j++) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The loop condition argvalues[i + j] will terminate the loop prematurely if an argument value is falsy (e.g., null or undefined). In JavaScript, variadic parameters should collect all remaining arguments even if they are null or undefined. It is safer to check for the existence of the index in the record.

Suggested change
for (let j = 0; argvalues[i + j]; j++) {
for (let j = 0; (i + j) in argvalues; j++) {

rest_val[j.toString()] = argvalues[i + j]
}

val = ObjectValue({
id: paramName,
field: rest_val,
})
} else {
if (!paramName) continue // unused parameters

let index = i
if (node.names && node.names.length > 0) {
// handle named argument values like "f({value: 2, key: 3})"
const k = node.names.indexOf(param.name)
if (k !== -1) index = k
const argKeys = Object.keys(argvalues)
if (argKeys.includes(paramName)) {
index = paramName
}
// if (DEBUG) logger.info('write arg:' + formatNode(param) + ' = ' + formatNode(argvalues[i]));

val = argvalues[index]
if (!val) {
continue
}
Comment on lines 2637 to +2640
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The check if (!val) will skip arguments that are null or undefined. While undefined might indicate a missing argument, null is a valid value that should be passed to the function. Additionally, if val is a PrimitiveValue object representing 0 or false, it will be truthy, but if the system ever passes raw primitives here, they would be incorrectly skipped. It is safer to check if the key exists in the record.

Suggested change
val = argvalues[index]
if (!val) {
continue
}
val = argvalues[index]
if (val === undefined && !(index in argvalues)) {
continue
}

}

// add source line information
Expand Down Expand Up @@ -2807,7 +2824,7 @@ class Analyzer extends MemSpace {
this.executeFunctionInArguments(scope, fclos, node, argvalues, state)
}
// save pass-in arguments for later use
if (argvalues.length > 0) {
if (Object.keys(argvalues).length > 0) {
if (!obj.arguments || (Array.isArray(obj.arguments) && obj.arguments?.length === 0)) {
obj.arguments = argvalues
} else {
Expand Down Expand Up @@ -2861,24 +2878,25 @@ class Analyzer extends MemSpace {
}
if (paras) {
if (paras.type === 'ParameterList') paras = paras.parameters
const len = Math.min(paras.length, argvalues.length)
for (let i = 0; i < len; i++) {
const param = paras[i]
let index = i
const names = node.names || node.arguments
if (names > 0) {
// handle named argument values like "f({value: 2, key: 3})"
const k = names.indexOf(param.name)
if (k !== -1) index = k
}
let val = argvalues[index]
// add source line information
if (param.loc) {
val = SourceLine.addSrcLineInfo(val, node, param.loc.sourcefile, 'CTOR ARG PASS: ', param.name)
const indices = Object.keys(paras)
for (const key in argvalues) {
let param
if (indices.includes(key)) {
param = paras[key]
} else {
param = paras.find((para: any) => para.id && para.id.name === key)
}

if (fdef.type === 'StructDefinition') {
this.saveVarInCurrentScope(obj, param, val, state)
if (param) {
let val = argvalues[key]
// add source line information
if (param.loc) {
val = SourceLine.addSrcLineInfo(val, node, param.loc.sourcefile, 'CTOR ARG PASS: ', param.name)
}

if (fdef.type === 'StructDefinition') {
this.saveVarInCurrentScope(obj, param, val, state)
}
}
}
}
Expand Down Expand Up @@ -2918,7 +2936,7 @@ class Analyzer extends MemSpace {
const needInvoke = Config.invokeCallbackOnUnknownFunction
if (needInvoke !== 1 && needInvoke !== 2) return UndefinedValue()

for (let i = 0; i < argvalues.length; i++) {
for (const i in argvalues) {
const arg = argvalues[i]
if (arg && arg.vtype === 'fclos') {
const fclos = _.clone(arg)
Expand Down
4 changes: 2 additions & 2 deletions src/engine/analyzer/common/entrypoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const constant = require('../../../util/constant')
export interface EntryPoint {
type?: string
scopeVal?: any
argValues?: any[]
argValues?: Record<number | string, any>
entryPointSymVal?: {
ast?: {
loc?: any
Expand All @@ -28,7 +28,7 @@ class EntryPointClass implements EntryPoint {

scopeVal: any

argValues: any[]
argValues: Record<number | string, any>

entryPointSymVal: any

Expand Down
Loading
Loading