diff --git a/src/abstract-interpretation/absint-visitor.ts b/src/abstract-interpretation/absint-visitor.ts index c25ed983830..7d7d29bd23e 100644 --- a/src/abstract-interpretation/absint-visitor.ts +++ b/src/abstract-interpretation/absint-visitor.ts @@ -15,7 +15,11 @@ import { AbstractDomain } from './domains/abstract-domain'; import type { AnyStateDomain, ValueDomain } from './domains/state-domain-like'; import { UnsupportedFunctions } from './unsupported-functions'; -export type AbsintVisitorConfiguration = Omit, 'defaultVisitingOrder' | 'defaultVisitingType'>; +export type AbsintVisitorConfiguration = Omit, 'defaultVisitingOrder' | 'defaultVisitingType'> + & { + /** Whether unsupported functions should be mapped to Top */ + ignoreUnsupportedFunctions?: boolean + }; /** * A control flow graph visitor to perform abstract interpretation. @@ -44,6 +48,8 @@ export abstract class AbstractInterpretationVisitor = new Set(); + private readonly absIntConfig: AbsintVisitorConfiguration; + /** * A map mapping assignments of replacement calls to their replacement calls for replacement calls that have already been visited but whose assignment has not yet been processed. */ @@ -51,7 +57,10 @@ export abstract class AbstractInterpretationVisitor = Definition extends TaintAnalysisDefinition ? Name : never; +export type TaintAnalysisName = Definition extends TaintAnalysisDefinition ? Name : never; export type TaintAnalysisDomain = Definition extends TaintAnalysisDefinition ? Domain : never; /** * Fluent builder class for defining new taint analyses. */ -export class TaintAnalysisDefinition { +export class TaintAnalysisDefinition { public readonly domain: Domain; public mapper: TaintMapper = []; public name: Name; + public config: Config | undefined; private _msg: string | undefined; @@ -18,9 +20,10 @@ export class TaintAnalysisDefinition): this { diff --git a/src/taint-analysis/builder/taint-analysis.ts b/src/taint-analysis/builder/taint-analysis.ts index 850c650a3b3..5f6ea9fcf2e 100644 --- a/src/taint-analysis/builder/taint-analysis.ts +++ b/src/taint-analysis/builder/taint-analysis.ts @@ -1,4 +1,4 @@ -import type { ReadonlyFlowrAnalysisProvider } from '../../project/flowr-analyzer'; +import type { FlowrAnalyzer, ReadonlyFlowrAnalysisProvider } from '../../project/flowr-analyzer'; import type { TaintAnalysisDefinition, TaintAnalysisDomain } from './taint-analysis-definition'; import type { AnyPredefinedTaintAnalysisName } from '../predefined/predefined'; import { predefinedTaintAnalyses } from '../predefined/predefined'; @@ -39,6 +39,7 @@ export class TaintAnalysis { const results: Map>> = new Map(); for(const def of this.defs) { const visitor = new TaintInferenceVisitor(def.domain, def.mapper, { + ...def.config, controlFlow: await this.analyzer.controlflow(), ctx: this.analyzer.inspectContext(), dfg: (await this.analyzer.dataflow()).graph, diff --git a/src/taint-analysis/function-mapper.ts b/src/taint-analysis/function-mapper.ts index f4a382281c0..112d25968b0 100644 --- a/src/taint-analysis/function-mapper.ts +++ b/src/taint-analysis/function-mapper.ts @@ -2,16 +2,12 @@ import type { RNode } from '../r-bridge/lang-4.x/ast/model/model'; import type { ParentInformation } from '../r-bridge/lang-4.x/ast/model/processing/decorate'; import { RType } from '../r-bridge/lang-4.x/ast/model/type'; import { Identifier } from '../dataflow/environments/identifier'; -import type { - AbstractValue, - AnyAbstractDomain -} from '../abstract-interpretation/domains/abstract-domain'; +import type { AbstractValue, AnyAbstractDomain } from '../abstract-interpretation/domains/abstract-domain'; import { VariableResolve } from '../config'; -import type { - FunctionParameterLocation } from '../abstract-interpretation/data-frame/mappers/arguments'; -import { getFunctionArgument - , getArgumentValue - , +import type { FunctionParameterLocation } from '../abstract-interpretation/data-frame/mappers/arguments'; +import { + getArgumentValue, + getFunctionArgument, getFunctionArguments } from '../abstract-interpretation/data-frame/mappers/arguments'; import type { DataflowGraph } from '../dataflow/graph/graph'; @@ -46,7 +42,13 @@ export function mapFnCallToTaint( } const functionName = Identifier.getName(node.functionName.content); - const mapping = mapper.find(m => Identifier.matches(m.identifier, functionName)); + const mapping = mapper.find(m => { + if(Identifier.is(m.identifier)) { + return Identifier.matches(m.identifier, functionName); + } else { + return m.identifier.find(s => Identifier.matches(s, functionName)); + } + }); if(mapping?.taint) { return { taint: mapping.taint }; @@ -75,7 +77,7 @@ export function mapFnCallToTaint( export type TaintMapper = TaintMapping[]; export type TaintMapping = { - identifier: Identifier; + identifier: Identifier | Identifier[]; } & ( | { taint: AbstractValue; condition?: TaintCondition } | { taint?: AbstractValue; condition: TaintCondition } diff --git a/src/taint-analysis/predefined/predefined.ts b/src/taint-analysis/predefined/predefined.ts index 2dc6cf23eaf..0b08686b811 100644 --- a/src/taint-analysis/predefined/predefined.ts +++ b/src/taint-analysis/predefined/predefined.ts @@ -1,9 +1,13 @@ import { scaleAnalysis } from './scale-analysis'; import type { TaintAnalysisDefinition, TaintAnalysisName } from '../builder/taint-analysis-definition'; +import { securityAnalysis } from './security-analysis'; +import { randomnessAnalysis } from './randomness-analysis'; export const predefinedTaintAnalyses = { - 'scale': scaleAnalysis, -} as const satisfies AnalysisMap<['scale']>; + 'scale': scaleAnalysis, + 'security': securityAnalysis, + 'randomness': randomnessAnalysis +} as const satisfies AnalysisMap<['scale', 'security', 'randomness']>; export type AnyPredefinedTaintAnalysisName = keyof typeof predefinedTaintAnalyses; export type AllPredefinedTaintAnalysisNames = [AnyPredefinedTaintAnalysisName]; diff --git a/src/taint-analysis/predefined/randomness-analysis.ts b/src/taint-analysis/predefined/randomness-analysis.ts new file mode 100644 index 00000000000..4257cf55c5b --- /dev/null +++ b/src/taint-analysis/predefined/randomness-analysis.ts @@ -0,0 +1,30 @@ +import { Bottom, Top } from '../../abstract-interpretation/domains/lattice'; +import { TaintAnalysisDefinition } from '../builder/taint-analysis-definition'; +import { FiniteDomainBuilder } from '../builder/domain'; +import { Identifier } from '../../dataflow/environments/identifier'; + +export const Random = Symbol('Random'); +export const Deterministic = Symbol('Deterministic'); + +export const randomnessDomain = new FiniteDomainBuilder() + .addLeqOrder(Bottom, [Random, Deterministic]) + .addLeqOrder(Random, Top) + .addLeqOrder(Deterministic, Top) + .build(); + +export const randomnessAnalysis = new TaintAnalysisDefinition('randomness', randomnessDomain) + .through([ { + identifier: Identifier.make('c'), + taint: Deterministic + }, + { + identifier: [ + 'jitter', 'sample', 'sample.int', 'arima.sim', 'kmeans', 'princomp', 'rcauchy', 'rchisq', 'rexp', + 'rgamma', 'rgeom', 'rlnorm', 'rlogis', 'rmultinom', 'rnbinom', 'rnorm', 'rpois', 'runif', 'pointLabel', + 'some', 'rbernoulli', 'rdunif', 'generateSeedVectors', + 'rbeta', 'rf', 'rhyper', 'rweibull', 'rt', 'rvonmises', 'rwilcox', 'rxor', 'rhyper', 'rmvnorm', + 'rsignrank', 'randomForest', + 'permuted', 'permute', 'shuffle', 'shuffleSet', 'data_shuffle', 'sample_frac', 'sample_n', 'slice_sample', + ], + taint: Random + }]); diff --git a/src/taint-analysis/predefined/security-analysis.ts b/src/taint-analysis/predefined/security-analysis.ts new file mode 100644 index 00000000000..0b3c844604c --- /dev/null +++ b/src/taint-analysis/predefined/security-analysis.ts @@ -0,0 +1,57 @@ +import { Bottom, Top } from '../../abstract-interpretation/domains/lattice'; +import { TaintAnalysisDefinition } from '../builder/taint-analysis-definition'; +import { FiniteDomainBuilder } from '../builder/domain'; +import { UserFunctions } from '../../queries/catalog/input-sources-query/input-source-functions'; + +export const UserInput = Symbol('User Input'); +export const NetworkInput = Symbol('Network Input'); +export const FileInput = Symbol('File Input'); + +type SecurityLatticeElements = [typeof UserInput, typeof NetworkInput, typeof FileInput]; + + +export const securityDomain = new FiniteDomainBuilder() + .addLeqOrder(Bottom, [UserInput, NetworkInput, FileInput]) + .addLeqOrder(UserInput, Top) + .addLeqOrder(NetworkInput, Top) + .addLeqOrder(FileInput, Top) + .build(); + +export const securityAnalysis = new TaintAnalysisDefinition('security', securityDomain) + .through([ + { + identifier: UserFunctions, + taint: UserInput + }, + { + identifier: ['read.table', 'read.csv', 'read.csv2', 'read.delim', 'read.delim2', 'readRDS', 'download.file', 'url', 'GET', 'POST', 'PUT', + 'DELETE', 'PATCH', 'HEAD', 'content', 'handle', 'get_callback', 'VERB', 'fread', 'gzcon', 'readlines', 'readLines', 'source', 'load', 'curl_download', + 'curl_fetch_memory', 'getURL', 'getForm', 'read_html', 'read_xml', 'html_nodes', 'html_text', 'fromJSON', 'read.xlsx', 'drive_download', 'drive_get', + 's3read_using', 's3write_using', 'storage_download', 'AnnotationHub', 'ExperimentHub', 'scan', + 'socketConnection', 'request', 'curl'], + taint: NetworkInput, + } + ]) + .to([ + { + identifier: [ + 'eval', 'evalq', + 'system', 'system2', 'shell', + 'dyn.load', + 'source', 'sys.source', + '.C', '.Fortran', '.External', + 'read.table', 'read.csv', 'read.csv2', 'read.delim', 'read.delim2', + 'read.rds', 'load', 'unserialize', 'parse', 'parse.file', + 'download.file', 'url', 'socketConnection', 'pipe', 'fifo', 'gzfile', 'bzfile', 'xzfile', + 'file.create', 'file.append', 'file.remove', 'file.rename', 'file.copy', + 'write.table', 'write.csv', 'write.csv2', 'write.delim', 'write.delim2', + 'save', 'saveRDS', 'serialize', 'dump', 'save.history', 'save.image', + 'pdf' + ], + condition: { + argTaints: [{ pos: 0 }], + condition: (_args, [taint]) => (taint === UserInput || taint === NetworkInput || taint === FileInput) ? Bottom : taint + } + } + ]) + .report('User input potentially flowing to output'); \ No newline at end of file diff --git a/src/taint-analysis/taint-visitor.ts b/src/taint-analysis/taint-visitor.ts index 283a5775f3c..94fc593bec4 100644 --- a/src/taint-analysis/taint-visitor.ts +++ b/src/taint-analysis/taint-visitor.ts @@ -18,7 +18,7 @@ export class TaintInferenceVisitor extends Abs private readonly fnCallMapper: TaintMapper; constructor(domain: Domain, fnCallMapper: TaintMapper, visitorConfig: AbsintVisitorConfiguration) { - super(visitorConfig, StateAbstractDomain.top(domain.top())); + super({ ...visitorConfig, ignoreUnsupportedFunctions: false }, StateAbstractDomain.top(domain.top())); this.domain = domain; this.fnCallMapper = fnCallMapper; }