Example #1
0
def get_addon_akismet_reports(user, user_agent, referrer, upload=None,
                              addon=None, data=None, existing_data=()):
    if not waffle.switch_is_active('akismet-spam-check'):
        return []
    assert addon or upload
    properties = ('name', 'summary', 'description')

    if upload:
        addon = addon or upload.addon
        data = data or Addon.resolve_webext_translations(
            parse_addon(upload, addon, user, minimal=True), upload)

    reports = []
    for prop in properties:
        locales = data.get(prop)
        if not locales:
            continue
        if isinstance(locales, dict):
            # Avoid spam checking the same value more than once by using a set.
            locale_values = set(locales.values())
        else:
            # It's not a localized dict, it's a flat string; wrap it anyway.
            locale_values = {locales}
        for comment in locale_values:
            if not comment or comment in existing_data:
                # We don't want to submit empty or unchanged content
                continue
            report = AkismetReport.create_for_addon(
                upload=upload, addon=addon, user=user, property_name=prop,
                property_value=comment, user_agent=user_agent,
                referrer=referrer)
            reports.append((prop, report))
    return reports
Example #2
0
def check_xpi_info(xpi_info, addon=None, xpi_file=None):
    from olympia.addons.models import Addon, DeniedGuid
    guid = xpi_info['guid']
    is_webextension = xpi_info.get('is_webextension', False)

    # If we allow the guid to be omitted we assume that one was generated
    # or existed before and use that one.
    # An example are WebExtensions that don't require a guid but we generate
    # one once they're uploaded. Now, if you update that WebExtension we
    # just use the original guid.
    if addon and not guid and is_webextension:
        xpi_info['guid'] = guid = addon.guid
    if not guid and not is_webextension:
        raise forms.ValidationError(ugettext('Could not find an add-on ID.'))

    if guid:
        current_user = core.get_user()
        if current_user:
            deleted_guid_clashes = Addon.unfiltered.exclude(
                authors__id=current_user.id).filter(guid=guid)
        else:
            deleted_guid_clashes = Addon.unfiltered.filter(guid=guid)

        if addon and addon.guid != guid:
            msg = ugettext(
                'The add-on ID in your manifest.json or install.rdf (%s) '
                'does not match the ID of your add-on on AMO (%s)')
            raise forms.ValidationError(msg % (guid, addon.guid))
        if (not addon and
                # Non-deleted add-ons.
            (
                Addon.objects.filter(guid=guid).exists() or
                # DeniedGuid objects for legacy deletions.
                DeniedGuid.objects.filter(guid=guid).exists() or
                # Deleted add-ons that don't belong to the uploader.
                deleted_guid_clashes.exists())):
            raise forms.ValidationError(ugettext('Duplicate add-on ID found.'))
    if len(xpi_info['version']) > 32:
        raise forms.ValidationError(
            ugettext('Version numbers should have fewer than 32 characters.'))
    if not VERSION_RE.match(xpi_info['version']):
        raise forms.ValidationError(
            ugettext('Version numbers should only contain letters, numbers, '
                     'and these punctuation characters: +*.-_.'))

    if is_webextension and xpi_info.get('type') == amo.ADDON_STATICTHEME:
        if not waffle.switch_is_active('allow-static-theme-uploads'):
            raise forms.ValidationError(
                ugettext(
                    'WebExtension theme uploads are currently not supported.'))

    if xpi_file:
        # Make sure we pass in a copy of `xpi_info` since
        # `resolve_webext_translations` modifies data in-place
        translations = Addon.resolve_webext_translations(
            xpi_info.copy(), xpi_file)
        verify_mozilla_trademark(translations['name'], core.get_user())

    return xpi_info
Example #3
0
def get_addon_akismet_reports(user,
                              user_agent,
                              referrer,
                              upload=None,
                              addon=None,
                              data=None):
    if not waffle.switch_is_active('akismet-spam-check'):
        return []
    assert addon or upload
    properties = ('name', 'summary', 'description')

    if upload:
        addon = addon or upload.addon
        data = data or Addon.resolve_webext_translations(
            parse_addon(upload, addon, user), upload)

    if not data:
        return []  # bail early if no data to skip Translation lookups
    if addon and addon.has_listed_versions():
        translation_ids_gen = (getattr(addon, prop + '_id', None)
                               for prop in properties)
        translation_ids = [id_ for id_ in translation_ids_gen if id_]
        # Just get all the values together to make it simplier
        existing_data = {
            text_type(value)
            for value in Translation.objects.filter(id__in=translation_ids)
        }
    else:
        existing_data = ()

    reports = []
    for prop in properties:
        locales = data.get(prop)
        if not locales:
            continue
        if isinstance(locales, dict):
            # Avoid spam checking the same value more than once by using a set.
            locale_values = set(locales.values())
        else:
            # It's not a localized dict, it's a flat string; wrap it anyway.
            locale_values = {locales}
        for comment in locale_values:
            if not comment or comment in existing_data:
                # We don't want to submit empty or unchanged content
                continue
            reports.append(
                AkismetReport.create_for_addon(upload=upload,
                                               addon=addon,
                                               user=user,
                                               property_name=prop,
                                               property_value=comment,
                                               user_agent=user_agent,
                                               referrer=referrer))
    return reports
Example #4
0
def get_addon_akismet_reports(user, user_agent, referrer, upload=None,
                              addon=None, data=None, existing_data=()):
    if not waffle.switch_is_active('akismet-spam-check'):
        return []
    assert addon or upload
    properties = ('name', 'summary', 'description')

    if upload:
        addon = addon or upload.addon
        if not data:
            try:
                data = Addon.resolve_webext_translations(
                    parse_addon(upload, addon, user, minimal=True), upload)
            except ValidationError:
                # The xpi is broken - it'll be rejected by the linter so abort.
                return []

    reports = []
    for prop in properties:
        locales = data.get(prop)
        if not locales:
            continue
        if isinstance(locales, dict):
            # Avoid spam checking the same value more than once by using a set.
            locale_values = set(locales.values())
        else:
            # It's not a localized dict, it's a flat string; wrap it anyway.
            locale_values = {locales}
        for comment in locale_values:
            if not comment or comment in existing_data:
                # We don't want to submit empty or unchanged content
                continue
            report = AkismetReport.create_for_addon(
                upload=upload, addon=addon, user=user, property_name=prop,
                property_value=comment, user_agent=user_agent,
                referrer=referrer)
            reports.append((prop, report))
    return reports
Example #5
0
def check_xpi_info(xpi_info, addon=None, xpi_file=None, user=None):
    from olympia.addons.models import Addon, DeniedGuid
    guid = xpi_info['guid']
    is_webextension = xpi_info.get('is_webextension', False)

    # If we allow the guid to be omitted we assume that one was generated
    # or existed before and use that one.
    # An example are WebExtensions that don't require a guid but we generate
    # one once they're uploaded. Now, if you update that WebExtension we
    # just use the original guid.
    if addon and not guid and is_webextension:
        xpi_info['guid'] = guid = addon.guid
    if not guid and not is_webextension:
        raise forms.ValidationError(ugettext('Could not find an add-on ID.'))

    if guid:
        current_user = core.get_user()
        if current_user:
            deleted_guid_clashes = Addon.unfiltered.exclude(
                authors__id=current_user.id).filter(guid=guid)
        else:
            deleted_guid_clashes = Addon.unfiltered.filter(guid=guid)

        if addon and addon.guid != guid:
            msg = ugettext(
                'The add-on ID in your manifest.json or install.rdf (%s) '
                'does not match the ID of your add-on on AMO (%s)')
            raise forms.ValidationError(msg % (guid, addon.guid))
        if (not addon and
                # Non-deleted add-ons.
            (
                Addon.objects.filter(guid=guid).exists() or
                # DeniedGuid objects for deletions for Mozilla disabled add-ons
                DeniedGuid.objects.filter(guid=guid).exists() or
                # Deleted add-ons that don't belong to the uploader.
                deleted_guid_clashes.exists())):
            raise forms.ValidationError(ugettext('Duplicate add-on ID found.'))
    if len(xpi_info['version']) > 32:
        raise forms.ValidationError(
            ugettext('Version numbers should have fewer than 32 characters.'))
    if not VERSION_RE.match(xpi_info['version']):
        raise forms.ValidationError(
            ugettext('Version numbers should only contain letters, numbers, '
                     'and these punctuation characters: +*.-_.'))

    if is_webextension and xpi_info.get('type') == amo.ADDON_STATICTHEME:
        max_size = settings.MAX_STATICTHEME_SIZE
        if xpi_file and os.path.getsize(xpi_file.name) > max_size:
            raise forms.ValidationError(
                ugettext(
                    u'Maximum size for WebExtension themes is {0}.').format(
                        filesizeformat(max_size)))

    if xpi_file:
        # Make sure we pass in a copy of `xpi_info` since
        # `resolve_webext_translations` modifies data in-place
        translations = Addon.resolve_webext_translations(
            xpi_info.copy(), xpi_file)
        verify_mozilla_trademark(translations['name'], core.get_user())

    # Parse the file to get and validate package data with the addon.
    if not acl.submission_allowed(user, xpi_info):
        raise forms.ValidationError(
            ugettext(u'You cannot submit this type of add-on'))

    if not addon and not system_addon_submission_allowed(user, xpi_info):
        guids = ' or '.join('"' + guid + '"'
                            for guid in amo.SYSTEM_ADDON_GUIDS)
        raise forms.ValidationError(
            ugettext(u'You cannot submit an add-on with a guid ending '
                     u'%s' % guids))

    if not mozilla_signed_extension_submission_allowed(user, xpi_info):
        raise forms.ValidationError(
            ugettext(u'You cannot submit a Mozilla Signed Extension'))

    return xpi_info
Example #6
0
def check_xpi_info(xpi_info, addon=None, xpi_file=None, user=None):
    from olympia.addons.models import Addon, DeniedGuid
    guid = xpi_info['guid']
    is_webextension = xpi_info.get('is_webextension', False)

    # If we allow the guid to be omitted we assume that one was generated
    # or existed before and use that one.
    # An example are WebExtensions that don't require a guid but we generate
    # one once they're uploaded. Now, if you update that WebExtension we
    # just use the original guid.
    if addon and not guid and is_webextension:
        xpi_info['guid'] = guid = addon.guid
    if not guid and not is_webextension:
        raise forms.ValidationError(ugettext('Could not find an add-on ID.'))

    if guid:
        current_user = core.get_user()
        if current_user:
            deleted_guid_clashes = Addon.unfiltered.exclude(
                authors__id=current_user.id).filter(guid=guid)
        else:
            deleted_guid_clashes = Addon.unfiltered.filter(guid=guid)

        if addon and addon.guid != guid:
            msg = ugettext(
                'The add-on ID in your manifest.json or install.rdf (%s) '
                'does not match the ID of your add-on on AMO (%s)')
            raise forms.ValidationError(msg % (guid, addon.guid))
        if (not addon and
            # Non-deleted add-ons.
            (Addon.objects.filter(guid=guid).exists() or
             # DeniedGuid objects for deletions for Mozilla disabled add-ons
             DeniedGuid.objects.filter(guid=guid).exists() or
             # Deleted add-ons that don't belong to the uploader.
             deleted_guid_clashes.exists())):
            raise forms.ValidationError(ugettext('Duplicate add-on ID found.'))
    if len(xpi_info['version']) > 32:
        raise forms.ValidationError(
            ugettext('Version numbers should have fewer than 32 characters.'))
    if not VERSION_RE.match(xpi_info['version']):
        raise forms.ValidationError(
            ugettext('Version numbers should only contain letters, numbers, '
                     'and these punctuation characters: +*.-_.'))

    if is_webextension and xpi_info.get('type') == amo.ADDON_STATICTHEME:
        if not waffle.switch_is_active('allow-static-theme-uploads'):
            raise forms.ValidationError(ugettext(
                'WebExtension theme uploads are currently not supported.'))

    if xpi_file:
        # Make sure we pass in a copy of `xpi_info` since
        # `resolve_webext_translations` modifies data in-place
        translations = Addon.resolve_webext_translations(
            xpi_info.copy(), xpi_file)
        verify_mozilla_trademark(translations['name'], core.get_user())

    # Parse the file to get and validate package data with the addon.
    if not acl.submission_allowed(user, xpi_info):
        raise forms.ValidationError(
            ugettext(u'You cannot submit this type of add-on'))

    if not addon and not system_addon_submission_allowed(
            user, xpi_info):
        guids = ' or '.join(
                '"' + guid + '"' for guid in amo.SYSTEM_ADDON_GUIDS)
        raise forms.ValidationError(
            ugettext(u'You cannot submit an add-on with a guid ending '
                     u'%s' % guids))

    if not mozilla_signed_extension_submission_allowed(user, xpi_info):
        raise forms.ValidationError(
            ugettext(u'You cannot submit a Mozilla Signed Extension'))

    return xpi_info
Example #7
0
def check_xpi_info(xpi_info, addon=None, xpi_file=None, user=None):
    from olympia.addons.models import Addon, DeniedGuid

    guid = xpi_info['guid']

    # If we allow the guid to be omitted we assume that one was generated
    # or existed before and use that one.
    # An example are WebExtensions that don't require a guid but we generate
    # one once they're uploaded. Now, if you update that WebExtension we
    # just use the original guid.
    if addon and not guid:
        xpi_info['guid'] = guid = addon.guid

    if guid:
        if user and waffle.switch_is_active('allow-deleted-guid-reuse'):
            deleted_guid_clashes = Addon.unfiltered.exclude(
                authors__id=user.id).filter(guid=guid)
        else:
            deleted_guid_clashes = Addon.unfiltered.filter(guid=guid)

        if addon and addon.guid != guid:
            msg = gettext('The add-on ID in your manifest.json (%s) '
                          'does not match the ID of your add-on on AMO (%s)')
            raise forms.ValidationError(msg % (guid, addon.guid))
        if not addon and (
                # Non-deleted add-ons.
                Addon.objects.filter(guid=guid).exists()
                # DeniedGuid objects for deletions for Mozilla disabled add-ons
                or DeniedGuid.objects.filter(guid=guid).exists()
                # Deleted add-ons that don't belong to the uploader (or deleted
                # add-ons period if `allow-deleted-guid-reuse` waffle switch is
                # inactive).
                or deleted_guid_clashes.exists()):
            raise forms.ValidationError(gettext('Duplicate add-on ID found.'))
    if len(xpi_info['version']) > 32:
        raise forms.ValidationError(
            gettext('Version numbers should have fewer than 32 characters.'))
    if not VERSION_RE.match(xpi_info['version']):
        raise forms.ValidationError(
            gettext('Version numbers should only contain letters, numbers, '
                    'and these punctuation characters: +*.-_.'))

    if xpi_info.get('type') == amo.ADDON_STATICTHEME:
        max_size = settings.MAX_STATICTHEME_SIZE
        if xpi_file and xpi_file.size > max_size:
            raise forms.ValidationError(
                gettext('Maximum size for WebExtension themes is {0}.').format(
                    filesizeformat(max_size)))

    if xpi_file:
        # Make sure we pass in a copy of `xpi_info` since
        # `resolve_webext_translations` modifies data in-place
        translations = Addon.resolve_webext_translations(
            xpi_info.copy(), xpi_file)
        verify_mozilla_trademark(translations['name'], user)

    # Parse the file to get and validate package data with the addon.
    if not acl.experiments_submission_allowed(user, xpi_info):
        raise forms.ValidationError(
            gettext('You cannot submit this type of add-on'))

    if not addon and not acl.reserved_guid_addon_submission_allowed(
            user, xpi_info):
        raise forms.ValidationError(
            gettext(
                'You cannot submit an add-on using an ID ending with this suffix'
            ))

    if not acl.mozilla_signed_extension_submission_allowed(user, xpi_info):
        raise forms.ValidationError(
            gettext('You cannot submit a Mozilla Signed Extension'))

    if (not addon and guid and guid.lower().endswith(amo.RESERVED_ADDON_GUIDS)
            and not xpi_info.get('is_mozilla_signed_extension')):
        raise forms.ValidationError(
            gettext(
                'Add-ons using an ID ending with this suffix need to be signed with '
                'privileged certificate before being submitted'))

    if not acl.langpack_submission_allowed(user, xpi_info):
        raise forms.ValidationError(
            gettext('You cannot submit a language pack'))

    if not acl.site_permission_addons_submission_allowed(user, xpi_info):
        raise forms.ValidationError(
            gettext('You cannot submit this type of add-on'))

    return xpi_info