/// <reference types="node" />


export interface AgentLib {
    Agent: AgentConstructor;
    constants: typeof constants;
    version: string;
}

declare let lib: AgentLib;
export default lib;

declare const constants: {
    RuleType: {
        'unsafe-file-upload': 1,
        'path-traversal': 2,
        'reflected-xss': 4,
        'sql-injection': 8,
        'cmd-injection': 16,
        'nosql-injection-mongo': 32,
        'bot-blocker': 64,
        'ssjs-injection': 128,
        'method-tampering': 256,
        'prototype-pollution': 512,
    },
    InputType: {
        CookieName: 1,
        CookieValue: 2,
        HeaderKey: 3,
        HeaderValue: 4,
        JsonKey: 5,
        JsonValue: 6,
        Method: 7,
        ParameterKey: 8,
        ParameterValue: 9,
        UriPath: 10,
        UrlParameter: 11,
        MultipartName: 12,
        XmlValue: 13,
        /** __(RASPv3 only)__ Not to be used with scoreAtom/scoreHeader/scoreBody */
        RawBody: 14
    },
    DbType: {
        DB2: 1,
        MySQL: 2,
        Oracle: 3,
        Postgres: 4,
        Sqlite: 5,
        SqlServer: 6,
        Unknown: 7
    }
}

type AgentConstructor = {
    new(): Agent;
}

type RuleBitMask = number;
type JsString = string | String | Buffer;

type PairedInputs = [string, string];
type ScoreRequestData = {
    cookies: PairedInputs[];
    headers: PairedInputs[];
    queries: string | PairedInputs[];
    uriPath: string;
}

type ScoreRequestResult = {
    trackRequest: boolean;
    resultsList: ScoreRequestFinding[];
}

type ScoreRequestFinding = {
    ruleId: RuleName;
    inputType: InputTypeName;
    path: string[];
    key: string;
    value: string;
    score: number;
    idsList: string[];
}

type CheckInjectionResult = {
    startIndex: number;
    endIndex: number;
    overrunIndex: number;
    boundaryIndex: number;
}

export type XssState = {
  inputs: ArrayBuffer;
  state: ArrayBuffer;
}

export type Agent = {
    /**
     * Scores multiple input types in a single request
     * @param rules - bitwise OR of RuleTypes to check
     * @param reqData - request data to score
     * @param options - EvalOptions if you want to prefer worth watching
     * @returns ScoreRequestResult
     */
    scoreRequestConnect(rules: RuleBitMask, reqData: ScoreRequestData, options?: EvalOptions): ScoreRequestResult;

    /**
     * Evaluate the body of a request after making a guess as to the format.
     * @param rules - bitwise OR of RuleTypes to check
     * @param body - request body to score
     * @param options - EvalOptions if you want to prefer worth watching
     * @returns array of findings if the value is not safe
     */
    scoreRequestBody(rules: RuleBitMask, body: JsString | Buffer, options?: EvalOptions): ScoreRequestResult;

    /**
     * Scores a single string value for any input type
     * @param rules - bitwise OR of RuleTypes to check
     * @param inputType - input type of the value
     * @param value - string to score
     * @param options - EvalOptions if you want to prefer worth watching
     * @returns array of findings if the value is not safe
     */
    scoreAtom(rules: RuleBitMask, value: JsString, inputType: InputTypeId, options?: EvalOptions): ScoreAtomFinding[];

    /**
     * Scores a single string value for a header input type
     * @param rules - bitwise OR of RuleTypes to check
     * @param key - header key
     * @param value - header value to score
     * @param options - EvalOptions if you want to prefer worth watching
     * @returns array of findings if the value is not safe
     */
    scoreHeader(rules: RuleBitMask, key: JsString, value: JsString, options?: EvalOptions): ScoreAtomFinding[];

    /**
     * Check if a shell command string contains a possible command injection in a specified substring.
     * @param index - index of the substring of the command to check
     * @param length - length of the string to check
     * @param command - command to check
     * @returns CheckCommandInjectionResult
     */
    checkCommandInjectionSink(index: number, length: number, command: JsString): CheckInjectionResult;

    /**
     * Check if a SQL query string contains a possible SQL injection in a specified substring.
     * @param index - index of the substring of the query to check
     * @param length - length of the substring to check
     * @param dbType - database type
     * @param query - query to check
     * @returns CheckInjectionResult
     */
    checkSqlInjectionSink(index: number, length: number, dbType: DbTypeId, query: JsString): CheckInjectionResult;

    /**
     * Check if a MongoDB query string contains a possible NoSQL injection in a specified substring.
     * @param command - command to check
     * @param index - index of the substring of the query to check
     * @param length - length of the substring to check
     * @returns boolean indicating if an SSJS attack is possible
     */
    checkSsjsInjectionSink(command: JsString, index: number, length: number): boolean;

    /**
     * Finds MongoDB comparators in a string
     * @param text - text to check
     */
    findMongoComparators(text: JsString): { path: string[] }[];

    /**
     * Return true if the text argument is a valid mongo query operator
     * @param text - text to check
     * @returns boolean indicating if the text is a valid mongo query operator
     * @see https://docs.mongodb.com/manual/reference/operator/query/
     */
    isMongoQueryType(text: JsString): boolean;

    /**
     * Determine if path is dangerous based on text semantics and whether or not it is in 'custom' code.
     * @param path - path to check
     * @param isCustomCode - boolean indicating if the path is in custom code
     * @returns boolean indicating if the path is dangerous
     */
    isDangerousPath(path: JsString, isCustomCode: boolean): boolean;

    /**
     * Determine if a path called by a shell command is trying to access a restricted or dangerous path.
     * @param path - path to check
     * @returns boolean indicating if trying to access a restricted or dangerous path
     */
    containsDangerousPath(path: JsString): boolean;

    /**
     * Determine if a shell command is trying to chain commands together and if so,
     * return the index of the chain command in the string.
     * returns -1 if chaining not found.
     * @param command - command to check
     * @returns index of the chained command in the string
     */
    indexOfChaining(command: JsString): number;

    /**
     * Check if a given HTTP method may have been tampered with because it's not one of the known standard HTTP methods.
     * @param method - HTTP method to check
     * @returns boolean indicating if the method may have been tampered with
     */
    isMethodTampering(method: JsString): boolean;

    /**
     * __(RASPv3 only)__
     * XSS sinks do not have a Protect analog, so this method should not be used by Protect agents.
     *
     * Determine if the any input is an XSS attack in the context of the body. This is a convenience
     * method for use when the body is a single chunk, so the caller doesn't have to create state.
     *
     * @param {JsString} body a string or a buffer encoded as utf8 containing the full response
     * body.
     * @param {string[]} inputs both the source and propagation history
     * @returns {boolean} true if any input is an XSS attack in the context of the body, else false.
     */
    isXssAttackInWholeBody(body: JsString, inputs: string[]): boolean;

    /**
     * __(RASPv3 only)__
     * XSS sinks do not have a Protect analog, so this method should not be used by Protect agents.
     *
     * Create the state object for isXssChunkedBodyAttack(). The object holds the state between calls to
     * isXssChunkedBodyAttack(). Each call to isXssChunkedBodyAttack() checks a chunk of the response body.
     * This function filters out inputs that do not contain at least one character that is required
     * for an XSS attack. This is checked in isXssChunkedBodyAttack() but if the caller wishes to check
     * and skip calling isXssChunkedBodyAttack(), then if XssState.inputs.byteLength <= 1, then all calls
     * to isXssChunkedBodyAttack() will return false.
     *
     * @param {string[]} inputs both the source and propagation history
     * @returns {XssState} the initial state to pass to isXssChunkedBodyAttack(body_chunk, state).
     */
    makeXssState(inputs: string[]): XssState;

    /**
     * __(RASPv3 only)__
     * XSS sinks do not have a Protect analog, so this method should not be used by Protect agents.
     *
     * Determine if the any input is an XSS attack in the context of the body. Each response must
     * have a separate state object, created by makeXssState(). The state will be updated each time
     * isXssChunkedBodyAttack() is called with the next chunk of the response body.
     *
     * @param {JsString} body a string or a buffer encoded as utf8 containing a chunk of the
     * response body.
     * @param {XssState} state the state object created by makeXssState(). It will be updated each call.
     * @returns {boolean} true if any input is an XSS attack in the context of the body, else false.
     */
    isXssAttackInChunkedBody(chunk: JsString, state: XssState): boolean;

    /**
     * __(RASPv3 only)__
     * XSS sinks do not have a Protect analog, so this method should not be used by Protect agents.
     *
     * Check the inputs for egregious XSS.
     *
     * This should ONLY be called with the SOURCE inputs, NOT with any values from the
     * propagation history. Call this BEFORE isXssWholeBodyAttack() or isXssChunkedBodyAttack().
     *
     * @param {string[]} inputs An array of the saved source inputs, NOT propagation history.
     * @returns {boolean} true if an egregious XSS attack found, else false.
     */
    isXssScriptOrIframeInUserInput(inputs: string[]): boolean;

    /**
     * __(RASPv3 only)__
     * Context-free SQLi sink evaluation for RASP v3 agents. This rule will check for tautologies
     * in SQL statements and is roughly analogous to semantic SQL.
     *
     * @param - query the SQL query to check
     * @returns - boolean indicating if the query contains tautologies
     */
    checkSqlInjectionContextFreeSink(query: JsString): boolean;
}

type EvalOptions = {
    preferWorthWatching: boolean;
}

type ScoreAtomFinding = {
    ruleId: RuleName;
    score: number;
    idsList: string[];
}

export type RuleType = typeof constants.RuleType[keyof typeof constants.RuleType];
export type RuleName = keyof typeof constants.RuleType;
export type InputTypeId = typeof constants.InputType[keyof typeof constants.InputType];
export type InputTypeName = keyof typeof constants.InputType;
export type DbTypeId = typeof constants.DbType[keyof typeof constants.DbType];


