Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  AbuseReporter.sys.mjs   Sprache: unbekannt

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";

// Maximum length of the string properties sent to the API endpoint.
const MAX_STRING_LENGTH = 255;

const AMO_SUPPORTED_ADDON_TYPES = [
  "extension",
  "theme",
  "sitepermission",
  "dictionary",
];

const PREF_ADDON_ABUSE_REPORT_URL = "extensions.addonAbuseReport.url";

const lazy = {};

ChromeUtils.defineESModuleGetters(lazy, {
  AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
  ClientID: "resource://gre/modules/ClientID.sys.mjs",
});

XPCOMUtils.defineLazyPreferenceGetter(
  lazy,
  "ADDON_ABUSE_REPORT_URL",
  PREF_ADDON_ABUSE_REPORT_URL
);

const ERROR_TYPES = Object.freeze([
  "ERROR_CLIENT",
  "ERROR_NETWORK",
  "ERROR_SERVER",
  "ERROR_UNKNOWN",
]);

export class AbuseReportError extends Error {
  constructor(errorType, errorInfo = undefined) {
    if (!ERROR_TYPES.includes(errorType)) {
      throw new Error(`Unexpected AbuseReportError type "${errorType}"`);
    }

    let message = errorInfo ? `${errorType} - ${errorInfo}` : errorType;

    super(message);
    this.name = "AbuseReportError";
    this.errorType = errorType;
    this.errorInfo = errorInfo;
  }
}

/**
 * Create an error info string from a fetch response object.
 *
 * @param {Response} response
 *        A fetch response object to convert into an errorInfo string.
 *
 * @returns {Promise<string>}
 *          The errorInfo string to be included in an AbuseReportError.
 */
async function responseToErrorInfo(response) {
  return JSON.stringify({
    status: response.status,
    responseText: await response.text().catch(() => ""),
  });
}

/**
 * A singleton used to manage abuse reports for add-ons.
 */
export const AbuseReporter = {
  getAMOFormURL({ addonId }) {
    return Services.urlFormatter
      .formatURLPref("extensions.abuseReport.amoFormURL")
      .replace(/%addonID%/g, addonId);
  },

  isSupportedAddonType(addonType) {
    return AMO_SUPPORTED_ADDON_TYPES.includes(addonType);
  },

  /**
   * Send an add-on abuse report using the AMO API. The data passed to this
   * method might be augmented with report data known by Firefox.
   *
   * @param {string} addonId
   * @param {{[key: string]: string|null}} data
   *        Abuse report data to be submitting to the AMO API along with the
   *        additional abuse report data known by Firefox.
   * @param {object} [options]
   * @param {string} [options.authorization]
   *        An optional value of an Authorization HTTP header to be set on the
   *        submission request.
   *
   * @returns {Promise<object>} Return a promise that resolves to the JSON AMO
   *          API response (or an error when something went wrong).
   */
  async sendAbuseReport(addonId, data, options = {}) {
    const rejectReportError = async (errorType, { response } = {}) => {
      // Leave errorInfo empty if there is no response or fails to be converted
      // into an error info object.
      const errorInfo = response
        ? await responseToErrorInfo(response).catch(() => undefined)
        : undefined;

      throw new AbuseReportError(errorType, errorInfo);
    };

    let abuseReport = { addon: addonId, ...data };

    // If the add-on is installed, augment the data with internal report data.
    const addon = await lazy.AddonManager.getAddonByID(addonId);
    if (addon) {
      const metadata = await AbuseReporter.getReportData(addon);
      abuseReport = { ...abuseReport, ...metadata };
    }

    const headers = { "Content-Type": "application/json" };
    if (options?.authorization?.length) {
      headers.authorization = options.authorization;
    }

    let response;
    try {
      response = await fetch(lazy.ADDON_ABUSE_REPORT_URL, {
        method: "POST",
        credentials: "omit",
        referrerPolicy: "no-referrer",
        headers,
        body: JSON.stringify(abuseReport),
      });
    } catch (err) {
      Cu.reportError(err);
      return rejectReportError("ERROR_NETWORK");
    }

    if (response.ok && response.status >= 200 && response.status < 400) {
      return response.json();
    }

    if (response.status >= 400 && response.status < 500) {
      return rejectReportError("ERROR_CLIENT", { response });
    }

    if (response.status >= 500 && response.status < 600) {
      return rejectReportError("ERROR_SERVER", { response });
    }

    return rejectReportError("ERROR_UNKNOWN", { response });
  },

  /**
   * Helper function that retrieves from an addon object all the data to send
   * as part of the submission request, besides the `reason`, `message` which are
   * going to be received from the submit method of the report object returned
   * by `createAbuseReport`.
   * (See https://addons-server.readthedocs.io/en/latest/topics/api/abuse.html)
   *
   * @param {AddonWrapper} addon
   *        The addon object to collect the detail from.
   *
   * @return {object}
   *         An object that contains the collected details.
   */
  async getReportData(addon) {
    const truncateString = text =>
      typeof text == "string" ? text.slice(0, MAX_STRING_LENGTH) : text;

    // Normalize addon_install_source and addon_install_method values
    // as expected by the server API endpoint. Returns null if the
    // value is not a string.
    const normalizeValue = text =>
      typeof text == "string"
        ? text.toLowerCase().replace(/[- :]/g, "_")
        : null;

    const installInfo = addon.installTelemetryInfo || {};

    const data = {
      addon: addon.id,
      addon_version: addon.version,
      addon_name: truncateString(addon.name),
      addon_summary: truncateString(addon.description),
      addon_install_origin:
        addon.sourceURI && truncateString(addon.sourceURI.spec),
      install_date: addon.installDate && addon.installDate.toISOString(),
      addon_install_source: normalizeValue(installInfo.source),
      addon_install_source_url:
        installInfo.sourceURL && truncateString(installInfo.sourceURL),
      addon_install_method: normalizeValue(installInfo.method),
    };

    switch (addon.signedState) {
      case lazy.AddonManager.SIGNEDSTATE_BROKEN:
        data.addon_signature = "broken";
        break;
      case lazy.AddonManager.SIGNEDSTATE_UNKNOWN:
        data.addon_signature = "unknown";
        break;
      case lazy.AddonManager.SIGNEDSTATE_MISSING:
        data.addon_signature = "missing";
        break;
      case lazy.AddonManager.SIGNEDSTATE_PRELIMINARY:
        data.addon_signature = "preliminary";
        break;
      case lazy.AddonManager.SIGNEDSTATE_SIGNED:
        data.addon_signature = "signed";
        break;
      case lazy.AddonManager.SIGNEDSTATE_SYSTEM:
        data.addon_signature = "system";
        break;
      case lazy.AddonManager.SIGNEDSTATE_PRIVILEGED:
        data.addon_signature = "privileged";
        break;
      case lazy.AddonManager.SIGNEDSTATE_NOT_REQUIRED:
        data.addon_signature = "not_required";
        break;
      default:
        data.addon_signature = `unknown: ${addon.signedState}`;
    }

    // Set "curated" as addon_signature on recommended addons
    // (addon.isRecommended internally checks that the addon is also
    // signed correctly).
    if (addon.isRecommended) {
      data.addon_signature = "curated";
    }

    data.client_id = await lazy.ClientID.getClientIdHash();

    data.app = AppConstants.platform === "android" ? "android" : "firefox";
    data.appversion = Services.appinfo.version;
    data.lang = Services.locale.appLocaleAsBCP47;
    data.operating_system = AppConstants.platform;
    data.operating_system_version = Services.sysinfo.getProperty("version");

    return data;
  },
};

[ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge