зеркало из https://github.com/mozilla/bedrock.git
Bug 1026184: Port /security to bedrock.
Add an update_security_advisories command to pull in and parse the advisroies from the git repository.
This commit is contained in:
Родитель
53470c32ff
Коммит
a730fe41da
|
@ -31,3 +31,4 @@ james.ini
|
|||
node_modules
|
||||
/media/js/test/test-results.xml
|
||||
bedrock/externalfiles/files_cache
|
||||
mofo_security_advisories
|
||||
|
|
|
@ -12,7 +12,7 @@ def cache_control_expires(num_hours):
|
|||
Set the appropriate Cache-Control and Expires headers for the given
|
||||
number of hours.
|
||||
"""
|
||||
num_seconds = num_hours * 60 * 60
|
||||
num_seconds = int(num_hours * 60 * 60)
|
||||
|
||||
def decorator(func):
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
from django.conf.urls import patterns, url
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from util import page
|
||||
import views
|
||||
|
@ -15,8 +14,7 @@ urlpatterns = patterns('',
|
|||
page('about', 'mozorg/about.html'),
|
||||
page('book', 'mozorg/book.html'),
|
||||
url('^credits/$', views.credits_view, name='mozorg.credits'),
|
||||
url('^credits/faq/$', TemplateView.as_view(template_name='mozorg/credits-faq.html'),
|
||||
name='mozorg.credits-faq'),
|
||||
page('credits/faq', 'mozorg/credits-faq.html'),
|
||||
url('^about/partnerships/$', views.partnerships, name='mozorg.partnerships'),
|
||||
page('about/partnerships/distribution', 'mozorg/partnerships-distribution.html'),
|
||||
page('about/history', 'mozorg/about/history.html'),
|
||||
|
|
|
@ -0,0 +1,254 @@
|
|||
# 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 errno
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from optparse import make_option
|
||||
from subprocess import check_call, Popen, PIPE, STDOUT
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from django.core.management.base import NoArgsCommand, BaseCommand
|
||||
from django.db import transaction
|
||||
|
||||
from dateutil.parser import parse as parsedate
|
||||
from raven.contrib.django.raven_compat.models import client as raven_client
|
||||
|
||||
from bedrock.security.models import Product, SecurityAdvisory
|
||||
from bedrock.security.utils import chdir, parse_md_file
|
||||
|
||||
|
||||
ADVISORIES_REPO = settings.MOFO_SECURITY_ADVISORIES_REPO
|
||||
ADVISORIES_PATH = settings.MOFO_SECURITY_ADVISORIES_PATH
|
||||
|
||||
GIT = getattr(settings, 'GIT_BIN', 'git')
|
||||
GIT_CLONE = (GIT, 'clone', ADVISORIES_REPO, ADVISORIES_PATH)
|
||||
GIT_PULL = (GIT, 'pull', '--ff-only', 'origin', 'master')
|
||||
GIT_GET_HASH = (GIT, 'rev-parse', 'HEAD')
|
||||
|
||||
SM_RE = re.compile('seamonkey', flags=re.IGNORECASE)
|
||||
FNULL = open(os.devnull, 'w')
|
||||
|
||||
|
||||
def fix_product_name(name):
|
||||
if 'seamonkey' in name.lower():
|
||||
name = SM_RE.sub('SeaMonkey', name, 1)
|
||||
|
||||
return name
|
||||
|
||||
|
||||
def mkdir_p(path):
|
||||
"""Mimic mkdir -p from *nix."""
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except OSError as exc:
|
||||
if exc.errno == errno.EEXIST and os.path.isdir(path):
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
@chdir(ADVISORIES_PATH)
|
||||
def git_pull():
|
||||
old_hash = get_current_git_hash()
|
||||
check_call(GIT_PULL, stdout=FNULL, stderr=STDOUT)
|
||||
new_hash = get_current_git_hash()
|
||||
return old_hash, new_hash
|
||||
|
||||
|
||||
@chdir(ADVISORIES_PATH)
|
||||
def git_diff(old_hash, new_hash):
|
||||
modified_files = []
|
||||
if old_hash != new_hash:
|
||||
proc = Popen((GIT, 'diff', '--name-only', old_hash, new_hash), stdout=PIPE)
|
||||
git_out = proc.communicate()[0].split()
|
||||
for mf in git_out:
|
||||
if not mf.endswith('.md'):
|
||||
continue
|
||||
modified_files.append(os.path.join(ADVISORIES_PATH, mf))
|
||||
|
||||
return modified_files
|
||||
|
||||
|
||||
@chdir(ADVISORIES_PATH)
|
||||
def update_repo():
|
||||
modified_files = []
|
||||
deleted_files = []
|
||||
mod_files = git_diff(*git_pull())
|
||||
for mf in mod_files:
|
||||
if os.path.exists(mf):
|
||||
modified_files.append(mf)
|
||||
else:
|
||||
deleted_files.append(mf)
|
||||
|
||||
return modified_files, deleted_files
|
||||
|
||||
|
||||
@chdir(ADVISORIES_PATH)
|
||||
def get_current_git_hash():
|
||||
p = Popen(GIT_GET_HASH, stdout=PIPE)
|
||||
return p.communicate()[0].strip()
|
||||
|
||||
|
||||
@transaction.commit_on_success
|
||||
def delete_files(filenames):
|
||||
ids = []
|
||||
for filename in filenames:
|
||||
match = re.search('(\d{4}-\d{2,3})\.md$', filename)
|
||||
ids.append(match.group(1))
|
||||
|
||||
SecurityAdvisory.objects.filter(id__in=ids).delete()
|
||||
|
||||
|
||||
@transaction.commit_on_success
|
||||
def add_or_update_advisory(data, html):
|
||||
"""
|
||||
Add or update an advisory in the database.
|
||||
|
||||
:param data: dict of metadata about the advisory
|
||||
:param html: HTML content of the advisory
|
||||
:return: SecurityAdvisory
|
||||
"""
|
||||
mfsa_id = data.pop('mfsa_id')
|
||||
year, order = [int(x) for x in mfsa_id.split('-')]
|
||||
kwargs = {
|
||||
'id': mfsa_id,
|
||||
'title': data.pop('title'),
|
||||
'impact': data.pop('impact'),
|
||||
'reporter': data.pop('reporter', None),
|
||||
'year': year,
|
||||
'order': order,
|
||||
'html': html,
|
||||
}
|
||||
datestr = data.pop('announced', None)
|
||||
if datestr:
|
||||
dateobj = parsedate(datestr).date()
|
||||
kwargs['announced'] = dateobj
|
||||
prodver_objs = []
|
||||
for productname in data.pop('fixed_in'):
|
||||
productname = fix_product_name(productname)
|
||||
productobj, created = Product.objects.get_or_create(name=productname)
|
||||
prodver_objs.append(productobj)
|
||||
|
||||
# discard products. we rely on fixed_in.
|
||||
data.pop('products', None)
|
||||
|
||||
if data:
|
||||
kwargs['extra_data'] = data
|
||||
|
||||
advisory = SecurityAdvisory(**kwargs)
|
||||
advisory.save()
|
||||
advisory.fixed_in.clear()
|
||||
advisory.fixed_in.add(*prodver_objs)
|
||||
return advisory
|
||||
|
||||
|
||||
def update_db_from_file(filename):
|
||||
"""
|
||||
Parse file for YAML and Markdown and update database.
|
||||
|
||||
:raises: KeyError or ValueError
|
||||
:param filename: path to markdown file.
|
||||
:return: SecurityAdvisory instance
|
||||
"""
|
||||
return add_or_update_advisory(*parse_md_file(filename))
|
||||
|
||||
|
||||
def clone_repo():
|
||||
mkdir_p(ADVISORIES_PATH)
|
||||
check_call(GIT_CLONE, stdout=FNULL, stderr=STDOUT)
|
||||
|
||||
|
||||
def get_all_md_files():
|
||||
return glob.glob(os.path.join(ADVISORIES_PATH, 'announce', '*', '*.md'))
|
||||
|
||||
|
||||
class Command(NoArgsCommand):
|
||||
help = 'Refresh database of MoFo Security Advisories.'
|
||||
lock_key = 'command-lock:update_security_advisories'
|
||||
option_list = BaseCommand.option_list + (
|
||||
make_option('--force',
|
||||
action='store_true',
|
||||
dest='force',
|
||||
default=False,
|
||||
help='Force updating files even if up-to-date.'),
|
||||
make_option('--quiet',
|
||||
action='store_true',
|
||||
dest='quiet',
|
||||
default=False,
|
||||
help='Do not print output to stdout.'),
|
||||
make_option('--skip-git',
|
||||
action='store_true',
|
||||
dest='no_git',
|
||||
default=False,
|
||||
help='No update, just import files (implies --force)'),
|
||||
)
|
||||
|
||||
def get_lock(self):
|
||||
lock = cache.get(self.lock_key)
|
||||
if not lock:
|
||||
cache.set(self.lock_key, True, 60)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def release_lock(self):
|
||||
cache.delete(self.lock_key)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
if self.get_lock():
|
||||
super(Command, self).handle(*args, **options)
|
||||
self.release_lock()
|
||||
|
||||
def handle_noargs(self, **options):
|
||||
quiet = options['quiet']
|
||||
force = options['force']
|
||||
no_git = options['no_git']
|
||||
if no_git:
|
||||
force = True
|
||||
cloned = False
|
||||
|
||||
def printout(msg, ending=None):
|
||||
if not quiet:
|
||||
self.stdout.write(msg, ending=ending)
|
||||
|
||||
if not no_git:
|
||||
if not os.path.exists(ADVISORIES_PATH):
|
||||
printout('Cloning repository.')
|
||||
clone_repo()
|
||||
cloned = True
|
||||
elif force:
|
||||
printout('Updating repository.')
|
||||
git_pull()
|
||||
|
||||
if force or cloned:
|
||||
printout('Reading all files.')
|
||||
modified_files = get_all_md_files()
|
||||
deleted_files = []
|
||||
else:
|
||||
printout('Updating repository and finding modified files.')
|
||||
modified_files, deleted_files = update_repo()
|
||||
|
||||
if modified_files:
|
||||
for mf in modified_files:
|
||||
try:
|
||||
update_db_from_file(mf)
|
||||
except (KeyError, ValueError) as e:
|
||||
print 'ERROR parsing %s: %s' % (mf, e)
|
||||
raven_client.captureException()
|
||||
continue
|
||||
if not quiet:
|
||||
sys.stdout.write('.')
|
||||
sys.stdout.flush()
|
||||
printout('\nUpdated {0} files.'.format(len(modified_files)))
|
||||
|
||||
if deleted_files:
|
||||
delete_files(deleted_files)
|
||||
printout('Deleted {0} files.'.format(len(deleted_files)))
|
||||
|
||||
if not modified_files and not deleted_files:
|
||||
printout('Nothing to update.')
|
|
@ -0,0 +1,82 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from south.utils import datetime_utils as datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding model 'Product'
|
||||
db.create_table(u'security_product', (
|
||||
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=50)),
|
||||
('slug', self.gf('django.db.models.fields.CharField')(max_length=50, db_index=True)),
|
||||
('product', self.gf('django.db.models.fields.CharField')(max_length=50)),
|
||||
('product_slug', self.gf('django.db.models.fields.SlugField')(max_length=50)),
|
||||
))
|
||||
db.send_create_signal(u'security', ['Product'])
|
||||
|
||||
# Adding model 'SecurityAdvisory'
|
||||
db.create_table(u'security_securityadvisory', (
|
||||
('id', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True, db_index=True)),
|
||||
('title', self.gf('django.db.models.fields.CharField')(max_length=200)),
|
||||
('impact', self.gf('django.db.models.fields.CharField')(max_length=100)),
|
||||
('reporter', self.gf('django.db.models.fields.CharField')(max_length=100, null=True)),
|
||||
('announced', self.gf('django.db.models.fields.DateField')(null=True)),
|
||||
('year', self.gf('django.db.models.fields.SmallIntegerField')()),
|
||||
('order', self.gf('django.db.models.fields.SmallIntegerField')()),
|
||||
('extra_data', self.gf('django.db.models.fields.TextField')(default='{}')),
|
||||
('html', self.gf('django.db.models.fields.TextField')()),
|
||||
('last_modified', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now, blank=True)),
|
||||
))
|
||||
db.send_create_signal(u'security', ['SecurityAdvisory'])
|
||||
|
||||
# Adding M2M table for field fixed_in on 'SecurityAdvisory'
|
||||
m2m_table_name = db.shorten_name(u'security_securityadvisory_fixed_in')
|
||||
db.create_table(m2m_table_name, (
|
||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
|
||||
('securityadvisory', models.ForeignKey(orm[u'security.securityadvisory'], null=False)),
|
||||
('product', models.ForeignKey(orm[u'security.product'], null=False))
|
||||
))
|
||||
db.create_unique(m2m_table_name, ['securityadvisory_id', 'product_id'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'Product'
|
||||
db.delete_table(u'security_product')
|
||||
|
||||
# Deleting model 'SecurityAdvisory'
|
||||
db.delete_table(u'security_securityadvisory')
|
||||
|
||||
# Removing M2M table for field fixed_in on 'SecurityAdvisory'
|
||||
db.delete_table(db.shorten_name(u'security_securityadvisory_fixed_in'))
|
||||
|
||||
|
||||
models = {
|
||||
u'security.product': {
|
||||
'Meta': {'ordering': "('slug',)", 'object_name': 'Product'},
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50'}),
|
||||
'product': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
|
||||
'product_slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'})
|
||||
},
|
||||
u'security.securityadvisory': {
|
||||
'Meta': {'ordering': "('-year', '-order')", 'object_name': 'SecurityAdvisory'},
|
||||
'announced': ('django.db.models.fields.DateField', [], {'null': 'True'}),
|
||||
'extra_data': ('django.db.models.fields.TextField', [], {'default': "'{}'"}),
|
||||
'fixed_in': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'advisories'", 'symmetrical': 'False', 'to': u"orm['security.Product']"}),
|
||||
'html': ('django.db.models.fields.TextField', [], {}),
|
||||
'id': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True', 'db_index': 'True'}),
|
||||
'impact': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'last_modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'blank': 'True'}),
|
||||
'order': ('django.db.models.fields.SmallIntegerField', [], {}),
|
||||
'reporter': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
|
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
|
||||
'year': ('django.db.models.fields.SmallIntegerField', [], {})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['security']
|
|
@ -0,0 +1,83 @@
|
|||
# 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/.
|
||||
|
||||
from django.db import models
|
||||
from django.template.defaultfilters import slugify
|
||||
from django.utils.functional import total_ordering
|
||||
|
||||
from django_extensions.db.fields import ModificationDateTimeField
|
||||
from django_extensions.db.fields.json import JSONField
|
||||
from funfactory.urlresolvers import reverse
|
||||
from product_details.version_compare import Version
|
||||
|
||||
|
||||
@total_ordering
|
||||
class Product(models.Model):
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
slug = models.CharField(max_length=50, db_index=True)
|
||||
product = models.CharField(max_length=50)
|
||||
product_slug = models.SlugField()
|
||||
|
||||
class Meta:
|
||||
ordering = ('slug',)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def name_tuple(self):
|
||||
name, vers = self.name.rsplit(None, 1)
|
||||
return name, Version(vers)
|
||||
|
||||
@property
|
||||
def version(self):
|
||||
return self.name_tuple[1]
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.name_tuple < other.name_tuple
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('security.product-version-advisories',
|
||||
kwargs={'slug': self.slug})
|
||||
|
||||
def save(self, force_insert=False, force_update=False,
|
||||
using=None, update_fields=None):
|
||||
self.product = self.name_tuple[0]
|
||||
self.product_slug = slugify(self.product)
|
||||
self.slug = '{0}-{1}'.format(self.product_slug, self.version)
|
||||
super(Product, self).save(force_insert, force_update,
|
||||
using, update_fields)
|
||||
|
||||
|
||||
class SecurityAdvisory(models.Model):
|
||||
id = models.CharField(max_length=8, primary_key=True, db_index=True)
|
||||
title = models.CharField(max_length=200)
|
||||
impact = models.CharField(max_length=100)
|
||||
reporter = models.CharField(max_length=100, null=True)
|
||||
announced = models.DateField(null=True)
|
||||
year = models.SmallIntegerField()
|
||||
order = models.SmallIntegerField()
|
||||
fixed_in = models.ManyToManyField(Product, related_name='advisories')
|
||||
extra_data = JSONField()
|
||||
html = models.TextField()
|
||||
last_modified = ModificationDateTimeField()
|
||||
|
||||
class Meta:
|
||||
ordering = ('-year', '-order')
|
||||
get_latest_by = 'last_modified'
|
||||
|
||||
def __unicode__(self):
|
||||
return u'MFSA {0}'.format(self.id)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('security.advisory', kwargs={'pk': self.id})
|
||||
|
||||
@property
|
||||
def impact_class(self):
|
||||
return self.impact.lower().split(None, 1)[0]
|
||||
|
||||
@property
|
||||
def products(self):
|
||||
prods_set = set(v.product for v in self.fixed_in.all())
|
||||
return sorted(prods_set)
|
|
@ -0,0 +1,25 @@
|
|||
{% extends "security/base.html" %}
|
||||
|
||||
{% block page_title %}Mozilla Foundation Secuirty Advisories{% endblock %}
|
||||
|
||||
{% block article %}
|
||||
<article class="section-content" itemscope itemtype="http://schema.org/Article">
|
||||
<header>
|
||||
<h1 itemprop="name">Mozilla Foundation Security Advisories</h1>
|
||||
</header>
|
||||
<div itemprop="articleBody">
|
||||
|
||||
{% include "security/partials/impact_key.html" %}
|
||||
|
||||
<ul>
|
||||
{% for group in advisories|groupby('announced')|reverse %}
|
||||
<li>{{ group.grouper|datetime }}<ul>
|
||||
{% for advisory in group.list %}
|
||||
<li><a class="{{ advisory.impact_class }}" href="{{ advisory.get_absolute_url() }}">{{ advisory.id }}</a>: {{ advisory.title|safe }}</li>
|
||||
{% endfor %}
|
||||
</ul></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</article>
|
||||
{% endblock %}
|
|
@ -0,0 +1,36 @@
|
|||
{% extends "security/base.html" %}
|
||||
|
||||
{% block page_title %}{{ advisory.title|striptags }}{% endblock %}
|
||||
|
||||
{% block article %}
|
||||
<article class="section-content" itemscope itemtype="http://schema.org/Article">
|
||||
<header>
|
||||
<h1 itemprop="name">{{ advisory.title|safe }}</h1>
|
||||
</header>
|
||||
<div itemprop="articleBody">
|
||||
|
||||
<dl>
|
||||
<dt>Fixed in</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
{% for prodver in advisory.fixed_in.all() %}
|
||||
<li>{{ prodver.name }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</dd>
|
||||
<dt>Products:</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
{% for prod in advisory.products %}
|
||||
<li>{{ prod }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</dd>
|
||||
<dt>Impact:</dt>
|
||||
<dd class="{{ advisory.impact_class }}">{{ advisory.impact }}</dd>
|
||||
</dl>
|
||||
|
||||
{{ advisory.html|safe }}
|
||||
</div>
|
||||
</article>
|
||||
{% endblock %}
|
|
@ -0,0 +1,47 @@
|
|||
{# 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/. -#}
|
||||
|
||||
{% extends "base-resp.html" %}
|
||||
|
||||
{% block extrahead %}
|
||||
{{ css('security') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block body_class %}sand{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<nav class="breadcrumbs">
|
||||
<a href="{{ url('mozorg.home') }}">{{ _('Home') }}</a> >
|
||||
<a href="{{ url('security.index') }}">{{ _('Mozilla Security') }}</a> >
|
||||
</nav>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block main_feature %}{% endblock %}
|
||||
{% block menu_bar %}{% endblock %}
|
||||
<main id="main-content">
|
||||
<div class="main-column">
|
||||
{% block article %}{% endblock %}
|
||||
</div>
|
||||
{% block sidebar %}
|
||||
<aside class="sidebar">
|
||||
{% block side_nav %}
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="{{ url('security.index') }}">Security Center</a></li>
|
||||
<li><a href="{{ url('security.advisories') }}">Security Advisories</a></li>
|
||||
<li><a href="{{ url('security.known-vulnerabilities') }}">Known Vulnerabilities</a></li>
|
||||
<li><a href="{{ url('security.bug-bounty') }}">Bug Bounty</a></li>
|
||||
<li><a href="http://blog.mozilla.com/security/">Security Blog</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
{% endblock %}
|
||||
{% block side_extra %}
|
||||
{% endblock %}
|
||||
</aside>
|
||||
{% endblock %}
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
||||
{% block email_form %}{% endblock %}
|
|
@ -0,0 +1,222 @@
|
|||
{% extends "security/base.html" %}
|
||||
|
||||
{% block page_title %}Mozilla Web Application Security Bug Bounty FAQ{% endblock %}
|
||||
|
||||
{% block article %}
|
||||
<article class="section-content" itemscope itemtype="http://schema.org/Article">
|
||||
<header>
|
||||
<h1 itemprop="name">Web Application Security Bug Bounty FAQ</h1>
|
||||
</header>
|
||||
<div itemprop="articleBody">
|
||||
<p>This FAQ attempts to answer various questions about how the
|
||||
Mozilla security bounty program relates to Web Applications.
|
||||
This FAQ covers web applications and if you are reporting an issue
|
||||
unrelated to a web application security vulnerability,
|
||||
(e.g. a security issue in the Firefox browser), please see the original:
|
||||
<a href="{{ url('security.bug-bounty-faq') }}">Security Bug Bounty Program FAQ.</a>
|
||||
For more about the background of the bug bounty program see the
|
||||
<a href="{{ url('security.bug-bounty') }}">official guidelines</a>
|
||||
governing the program.</p>
|
||||
|
||||
<h3>General questions</h3>
|
||||
<ul>
|
||||
<li><a href="#why">Why do we include web applications as part
|
||||
of our security Bug Bounty Program?</a></li>
|
||||
<li><a href="#howto">How can I find potential vulnerabilities
|
||||
and are there things I shouldn't do in trying to find them?</a></li>
|
||||
<li><a href="#bounty-amount">Is the amount of the bounty
|
||||
different for web applications vulnerabilities?</a></li>
|
||||
<li><a href="#severity-ratings">Is there a list of severity ratings
|
||||
with examples?</a></li>
|
||||
</ul>
|
||||
|
||||
<h3>Eligible bugs</h3>
|
||||
<ul>
|
||||
<li><a href="#eligible-bugs">Which domains and web applications will be
|
||||
considered to be part of the bug bounty?</a></li>
|
||||
<li><a href="#issues">What types of issues will be considered as
|
||||
part of the bounty program?</a></li>
|
||||
<li><a href="#dos-bugs">Why won't you provide a reward for denial of service
|
||||
bugs?</a></li>
|
||||
<li><a href="#sites-not-listed">What about sites which are not listed?</a></li>
|
||||
</ul>
|
||||
<h3>Bug reporting</h3>
|
||||
<ul>
|
||||
<li><a href="#what-next">Once I have found a vulnerability,
|
||||
what next?</a></li>
|
||||
<li><a href="#nondisclosure">If I report the bug directly to you,
|
||||
do I have to keep the bug confidential and not publish information
|
||||
about it in order to receive a reward?</a></li>
|
||||
<li><a href="#cooperation">I don't have the time or desire to work
|
||||
with you further in investigating and fixing the bug; can I still get a
|
||||
bug bounty reward?</a></li>
|
||||
</ul>
|
||||
<h2>General questions</h2>
|
||||
<dl class="qandaset">
|
||||
<dt class="question" id="why">Why do we include web applications
|
||||
as part of our security Bug Bounty Program?</dt>
|
||||
<dd class="answer">
|
||||
<p>Many people are not aware that we have paid the bounty in the past on
|
||||
web application security vulnerabilities which impact client security.
|
||||
We are, however, expanding the bounty beyond web vulnerabilities which
|
||||
just affect the client. We also feel we should have a more formal
|
||||
structure around our web properties when it comes to paying a bounty
|
||||
because our goal is to make our products and services more secure.</p>
|
||||
</dd>
|
||||
<dt class="question" id="howto">How can I find potential vulnerabilities
|
||||
and are there things I shouldn't do in trying to find them?</dt>
|
||||
<dd class="answer">
|
||||
<p>We have received many questions around how to find web application
|
||||
security issues. The most common concern is the use of automatic tools.
|
||||
We ask that people don't use automatic tools against our web services
|
||||
as it is important to maintain our services and availability. We do
|
||||
encourage people to examine typical areas for vulnerabilities such as
|
||||
authentication and session management. Since our code is opensource, you are
|
||||
encourage to run on the software on your own server instance or just look
|
||||
at the source code for potential issues.
|
||||
(See the list below for the domains and applications which are in scope.)</p>
|
||||
</dd>
|
||||
<dt class="question" id="bounty-amount">Is the amount of the bounty
|
||||
different for web applications vulnerabilities?</dt>
|
||||
<dd class="answer">
|
||||
<p>In the past we have not formally paid the bug bounty on web vulnerabilities
|
||||
but we have paid the bounty for critical and extraordinary web application
|
||||
vulnerabilities.
|
||||
We are now going to include high severity web applications vulnerabilities.
|
||||
So we are giving a range starting at $500 (US) for high severity and, in some
|
||||
cases,
|
||||
may pay up to $3000 (US) for extraordinary or critical vulnerabilities.
|
||||
</p>
|
||||
</dd>
|
||||
<dt class="question" id="severity-ratings">Is there a list of severity
|
||||
ratings
|
||||
with examples?</dt>
|
||||
<dd class="answer">
|
||||
<p>Yes, here it is:
|
||||
<a href="https://wiki.mozilla.org/WebAppSec/Web_App_Severity_Ratings">
|
||||
Web Application Severity Ratings</a></p>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<h2>Eligible bugs</h2>
|
||||
<dl class="qandaset">
|
||||
<dt class="question" id="eligible-bugs">Which domains and web applications
|
||||
will be considered to be part of the bug bounty?</dt>
|
||||
<dd class="answer">
|
||||
<p>Below is the list of domains under scope for Mozilla's web application
|
||||
security
|
||||
bug bounty. While this list doesn't include all the Mozilla web properties,
|
||||
we do plan on adding to this list as we introduce our web security process to
|
||||
other sites not included in this list.</p>
|
||||
<ul>
|
||||
<li>bugzilla.mozilla.org</li>
|
||||
<li>*.services.mozilla.com</li>
|
||||
<li>aus*.mozilla.org</li>
|
||||
<li>www.mozilla.com/org</li>
|
||||
<li>www.firefox.com</li>
|
||||
<li>www.getfirefox.com</li>
|
||||
<li>addons.mozilla.org</li>
|
||||
<li>services.addons.mozilla.org</li>
|
||||
<li>versioncheck.addons.mozilla.org</li>
|
||||
<li>pfs.mozilla.org</li>
|
||||
<li>download.mozilla.org</li>
|
||||
</ul>
|
||||
|
||||
<p>The Mozilla Developer Network (developer.mozilla.org) has been removed from this
|
||||
list due to site vandalism by researchers looking for security issues. It will be
|
||||
added back at a later date when a test server is deployed to avoid negative impacts
|
||||
on our primary reference site.</p>
|
||||
|
||||
</dd>
|
||||
<br>
|
||||
<dt class="question" id="sites-not-listed">What about sites which are not
|
||||
listed?</dt>
|
||||
<dd class="answer">
|
||||
<p>If you find an issue with a site which is not "officially" part under the
|
||||
web application bug bounty, we would still like to know. If the bug is
|
||||
extraordinary, we might still consider the bug to be nominated for a bounty. In
|
||||
the past we have paid for interesting bugs which are outside of normal
|
||||
policy.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="qandaset">
|
||||
<dt class="question" id="issues">What types of issues will be considered
|
||||
as part of the bounty program?</dt>
|
||||
<dd class="answer">
|
||||
<p>While there are many types of web vulnerabilities, the list below directly
|
||||
affects our users and our infrastructure. These are just a few examples.</p>
|
||||
<ul>
|
||||
<li>Cross-Site Scripting (XSS)</li>
|
||||
<li>Cross-Site Request Forgery (CSRF)</li>
|
||||
<li>Injection / RFI/LFI</li>
|
||||
</ul>
|
||||
<p>The
|
||||
<a href="https://wiki.mozilla.org/WebAppSec/Web_App_Severity_Ratings">
|
||||
Web Application Severity Ratings</a> is consider to be the master list
|
||||
for what will be part of the bug bounty. </p>
|
||||
</dd>
|
||||
<dt class="question" id="dos-bugs">Why won't you provide a reward
|
||||
for denial of service bugs?</dt>
|
||||
<dd class="answer">
|
||||
<p>Because DoS bugs are generally less serious than other web application
|
||||
security bugs and in many cases a DoS doesn't need to involve a technical
|
||||
vulnerability within a web application. We have decided to concentrate our
|
||||
limited resources on rewarding people who find what we consider to be more
|
||||
serious security problems.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<h2>Bug reporting</h2>
|
||||
<dl class="qandaset">
|
||||
<dt class="question" id="what-next">Once I have found a
|
||||
vulnerability, what next?</dt>
|
||||
<dd class="answer">
|
||||
<p>Please <a href="https://bugzilla.mozilla.org/enter_bug.cgi">
|
||||
file a bug</a> describing the security bug. The security check-box
|
||||
should also be checked when filing the bug. Further details on the security
|
||||
check-box can be found
|
||||
<a
|
||||
href=https://wiki.mozilla.org/WebAppSec/Web_App_Severity_Ratings#
|
||||
Security_Group_Settings>here.</a>
|
||||
</p>
|
||||
|
||||
<p>We also ask that you notify the <a
|
||||
href="mailto:security@mozilla.org?subject=Web%20Security%20Bug%20Bounty:<
|
||||
DESCRIPTION>">
|
||||
Mozilla Security Group</a> by email and include the bug number and a brief
|
||||
summary.
|
||||
</p>
|
||||
</dd>
|
||||
<dt class="question" id="nondisclosure">If I report the bug
|
||||
directly to you, do I have to keep the bug confidential and not publish
|
||||
information about it in order to receive a reward?</dt>
|
||||
<dd class="answer">
|
||||
<p>No. We're rewarding you for finding a bug, not trying to buy
|
||||
your silence. However if you report the bug through the standard
|
||||
Mozilla process and haven't already published information about it then
|
||||
we do ask that you follow the guidelines set forth in the official
|
||||
policy on <a href="{{ url('mozorg.about.governance.policies.security.bugs') }}">handling
|
||||
Mozilla security bugs</a>. Under this policy security-sensitive bug
|
||||
reports in our Bugzilla system may be kept private for a limited period
|
||||
of time to give us a chance to fix the bug before the bug is made
|
||||
public, with an option for the bug reporter (or others) to open the bug
|
||||
to public view earlier whenever circumstances warrant it (e.g., if you feel your
|
||||
bug report is being completely ignored).However, in the interest of
|
||||
protecting our users, we would appreciate a reasonable amount of
|
||||
time to address the issue before the information is publicly disclosed.</p>
|
||||
</dd>
|
||||
<dt class="question" id="cooperation">I don't have the time or
|
||||
desire to work with you further in investigating and fixing the bug;
|
||||
can I still get a bug bounty reward?</dt>
|
||||
<dd class="answer">
|
||||
<p>Yes. Again, we're rewarding you for finding a bug, not trying to
|
||||
buy your cooperation. However we do invite you to work together with
|
||||
us, and we hope that you'll accept that offer in the spirit in which it
|
||||
was intended. In return you'll get the opportunity to work as a full
|
||||
member of the team fixing your bug and see "from the inside" exactly
|
||||
how Mozilla security bugs get resolved.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</article>
|
||||
{% endblock %}
|
|
@ -0,0 +1,283 @@
|
|||
{% extends "security/base.html" %}
|
||||
|
||||
{% block page_title %}Mozilla Security Bug Bounty Program{% endblock %}
|
||||
|
||||
{% block article %}
|
||||
<article class="section-content" itemscope itemtype="http://schema.org/Article">
|
||||
<header>
|
||||
<h1 itemprop="name">Bug Bounty Program FAQ</h1>
|
||||
</header>
|
||||
<div itemprop="articleBody">
|
||||
<p>This FAQ attempts to answer various questions about the Mozilla
|
||||
security bug bounty program sponsored by the Mozilla Foundation. For
|
||||
more information see the <a href="https://blog.mozilla.org/press/2004/08/mozilla-foundation-announces-security-bug-bounty-program/">original
|
||||
announcement</a> and the <a href="{{ url('security.bug-bounty') }}">official
|
||||
guidelines</a> governing the program.</p>
|
||||
|
||||
<h3>General questions</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="#why">Why is the Mozilla Foundation doing this?</a></li>
|
||||
<li><a href="#eligibility">Are Mozilla developers eligible for the bug bounty
|
||||
reward?</a></li>
|
||||
</ul>
|
||||
|
||||
<h3>Eligible software</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="#other-products">Can I get the bug bounty reward if I
|
||||
discover a bug in Camino, Galeon, K-Meleon, Netscape 7, or other
|
||||
products based on Mozilla code?</a></li>
|
||||
<li><a href="#bugzilla-etc">Does the bug bounty cover bugs found in
|
||||
Bugzilla, Tinderbox, Bonsai, and other software created and distributed
|
||||
as part of the Mozilla project?</a></li>
|
||||
<li><a href="#most-recent">What do you mean by the "most recent
|
||||
version" of Firefox, Firefox for Android, and/or Thunderbird?</a></li>
|
||||
<li><a href="#older-releases">Can I get the bug bounty reward if I
|
||||
discover a bug in an older release of Firefox, Firefox for Android,
|
||||
and/or Thunderbird?</a></li>
|
||||
<li><a href="#development-releases">Can I get the bug bounty reward if I
|
||||
discover a bug in a pre-release version of Firefox, Firefox for Android, or
|
||||
Thunderbird?</a></li>
|
||||
<li><a href="#third-party-releases">Can I get the bug bounty reward
|
||||
if I discover a bug that occurs in a third-party release of Firefox,
|
||||
Firefox for Android, and/or Thunderbird (e.g., a localized build, optimized
|
||||
build, or third-party Firefox, Firefox for Android, or Thunderbird
|
||||
distribution)?</a></li>
|
||||
<li><a href="#platform-specific">Can I get the bug bounty reward if I
|
||||
discover a bug that occurs only on a particular operating system?</a></li>
|
||||
</ul>
|
||||
|
||||
<h3>Eligible bugs</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="#eligible-bugs">What types of security bugs are eligible?</a></li>
|
||||
<li><a href="#dos-bugs">Why won't you provide a reward for denial of
|
||||
service bugs?</a></li>
|
||||
</ul>
|
||||
|
||||
<h3>Bug reporting, etc.</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="#already-published">I've already published information
|
||||
about the bug, and didn't go through the Mozilla bug process; can I
|
||||
still get a reward?</a></li>
|
||||
<li><a href="#nondisclosure">If I report the bug directly to you, do
|
||||
I have to keep the bug confidential and not publish information about
|
||||
it in order to receive a reward?</a></li>
|
||||
<li><a href="#cooperation">I don't have the time or desire to work
|
||||
with you further in investigating and fixing the bug; can I still get a
|
||||
bug bounty reward?</a></li>
|
||||
</ul>
|
||||
|
||||
<h2>General questions</h2>
|
||||
|
||||
<dl class="qandaset">
|
||||
<dt class="question" id="why">Why is the Mozilla Foundation doing
|
||||
this?</dt>
|
||||
|
||||
<dd class="answer">
|
||||
<p>Because we want to encourage more people to find and report
|
||||
security bugs in our products, so that we can make our products even
|
||||
more secure than they already are. It's as simple as that.</p>
|
||||
</dd>
|
||||
|
||||
<dt class="question" id="eligibility">Are Mozilla developers
|
||||
eligible for the bug bounty reward?</dt>
|
||||
|
||||
<dd class="answer">
|
||||
<p>Yes. Anyone is eligible to receive a reward except for employees of
|
||||
the Mozilla Foundation and its subsidiaries and the creators and
|
||||
reviewers of the code in which the bug was found. However, as noted in
|
||||
the policy, if you found this bug as part of your job (in other words,
|
||||
while being paid to work on Mozilla) then we'd appreciate it if you
|
||||
would not apply for the bounty, in order to preserve our limited funds
|
||||
for rewarding volunteer contributors.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<h2>Eligible software</h2>
|
||||
|
||||
<dl class="qandaset">
|
||||
<dt class="question" id="other-products">Can I get the bug
|
||||
bounty reward if I discover a bug in Camino, Galeon, K-Meleon, Netscape
|
||||
7, Mozilla Suite, SeaMonkey, or other products based on Mozilla code?</dt>
|
||||
|
||||
<dd class="answer">
|
||||
<p>Only if we can reproduce the problem in the most recent version
|
||||
of Firefox, Firefox for Android, and/or Thunderbird.</p>
|
||||
</dd>
|
||||
|
||||
<dt class="question" id="bugzilla-etc">Does the bug bounty cover
|
||||
bugs found in Bugzilla, Tinderbox, Bonsai, and other software created
|
||||
and distributed as part of the Mozilla project?</dt>
|
||||
|
||||
<dd class="answer">
|
||||
<p>No. We have decided to use our limited resources to focus on our
|
||||
end-user products, as opposed to the other software produced and used
|
||||
by the Mozilla project.</p>
|
||||
</dd>
|
||||
|
||||
<dt class="question" id="most-recent">What do you mean by the
|
||||
"most recent version" of Firefox, Firefox for Android, and/or
|
||||
Thunderbird?</dt>
|
||||
|
||||
<dd class="answer">
|
||||
<p>In general we mean the releases available for download on the <a
|
||||
href="{{ url('mozorg.products') }}">mozilla.org download pages</a> at the time the
|
||||
bug was reported. However we will also consider paying rewards for
|
||||
security bugs as discussed in the questions and answers below.</p>
|
||||
</dd>
|
||||
|
||||
<dt class="question" id="older-releases">Can I get the bug
|
||||
bounty reward if I discover a bug in an older release of Firefox,
|
||||
Firefox for Android, and/or Thunderbird?</dt>
|
||||
|
||||
<dd class="answer">
|
||||
<p>In general bugs found in earlier releases are eligible for a
|
||||
reward only if we can reproduce the problem using the most recent
|
||||
version.</p>
|
||||
|
||||
<p>However as a special exception we will also consider paying
|
||||
rewards for bugs found in the most recent releases from designated
|
||||
stable branches (e.g., from the Mozilla 17 Extended Support Release branch while
|
||||
supported) if the bugs are not present in the most recent version but
|
||||
were never recognized and fixed as security bugs. (For example, the bug
|
||||
might be in code associated with a feature that was removed and/or
|
||||
heavily modified in the most recent version, and might have been
|
||||
"fixed" solely as a byproduct of other unrelated changes.)</p>
|
||||
</dd>
|
||||
|
||||
<dt class="question" id="development-releases">Can I get the bug bounty
|
||||
reward if I discover a bug in a pre-release version of Firefox, Firefox for
|
||||
Android, or Thunderbird?</dt>
|
||||
|
||||
<dd class="answer">
|
||||
<p>Yes, as long as the bug otherwise meets the published bug bounty
|
||||
program guidelines. Bugs found in Aurora and Beta releases of Firefox and
|
||||
Firefox for Android, EarlyBird and Beta releases of Thunderbird are eligible as
|
||||
long the bug is reproducible in the latest nightly mozilla-central build and has
|
||||
not been previously reported.</p>
|
||||
</dd>
|
||||
|
||||
<dt class="question" id="third-party-releases">Can I get the bug
|
||||
bounty reward if I discover a bug that occurs in a third-party release
|
||||
of Firefox, Firefox for Android, and/or Thunderbird (e.g., a localized
|
||||
build, optimized build, or third-party Firefox, Firefox for Android, or
|
||||
Thunderbird distribution)?</dt>
|
||||
|
||||
<dd class="answer">
|
||||
<p>Yes, if the bug can be reproduced in an official Mozilla
|
||||
Foundation release and otherwise meets the published guidelines.</p>
|
||||
</dd>
|
||||
|
||||
<dt class="question" id="platform-specific">Can I get the bug
|
||||
bounty reward if I discover a bug that occurs only on a particular
|
||||
operating system?</dt>
|
||||
|
||||
<dd class="answer">
|
||||
<p>Yes, if the operating system is officially supported by the most
|
||||
recent version of the product for which you're reporting the bug. (For
|
||||
a list of supported operating systems and hardware configurations see
|
||||
the system requirements for the
|
||||
<a href="{{ url('firefox.sysreq') }}">Firefox
|
||||
</a>, <a href="https://support.mozilla.org/kb/will-firefox-work-my-mobile-device">Firefox for
|
||||
Android</a>, and
|
||||
{# There isn't a good url for latest Tbird sys-req. #}
|
||||
{# TODO Make a latest Tbird sys-req url. #}
|
||||
<a href="/thunderbird/">Thunderbird</a>.)</p>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<h2>Eligible bugs</h2>
|
||||
|
||||
<dl class="qandaset">
|
||||
<dt class="question" id="eligible-bugs">What types of security bugs are
|
||||
eligible?</dt>
|
||||
|
||||
<dd class="answer">
|
||||
<p>Reproducible security bugs that are determined to be rated
|
||||
<a href="https://wiki.mozilla.org/Security_Severity_Ratings">sec-critical or
|
||||
sec-high</a> are eligible. In general we consider critical security bugs to be
|
||||
those that allow execution of arbitrary code on users' systems, while high
|
||||
severity security bugs allow access to users' confidential information. In the
|
||||
latter case we consider bugs to be sec-high only if they potentially expose
|
||||
high-value personal information (e.g., passwords, credit card numbers, and the
|
||||
like); in the context of the bug bounty program we do not consider bugs to be
|
||||
sec-high if they potentially expose only lower-value information (e.g., browsing
|
||||
history) or information that would be useful primarily for other exploits
|
||||
(e.g., the names of files or directories on the user's system).</p>
|
||||
|
||||
<p>Finally, in general we do not consider bugs that permit only denial of
|
||||
service attacks to be eligible in the sense described above.</p>
|
||||
</dd>
|
||||
|
||||
<dt class="question" id="dos-bugs">Why won't you provide a
|
||||
reward for denial of service (DoS) bugs?</dt>
|
||||
|
||||
<dd class="answer">
|
||||
<p>Because DoS bugs are generally less serious than other security
|
||||
bugs (e.g., they typically do not lead to corruption or destruction of
|
||||
user data, much less theft of data), and in many cases a DoS attack
|
||||
does not involve an actual bug but simply misuse of standard product
|
||||
features (e.g., putting up a web site with an excessive number of
|
||||
graphics, sending excessively long mail messages, etc.). We have
|
||||
decided to concentrate our limited resources on rewarding people who
|
||||
find what we consider to be more serious security problems.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<h2>Bug reporting, etc.</h2>
|
||||
|
||||
<dl class="qandaset">
|
||||
<dt class="question" id="already-published">I've already
|
||||
published information about the bug, and didn't go through the Mozilla
|
||||
bug process; can I still get a reward?</dt>
|
||||
|
||||
<dd class="answer">
|
||||
<p>Yes, as long as the bug report occurred after the official
|
||||
announcement of the bug bounty program on August 2, 2004, and otherwise
|
||||
meets the published bug bounty program guidelines (e.g., the bug has
|
||||
not been reported previously and is reproducible in the most recent
|
||||
version of the affected product).</p>
|
||||
|
||||
<p>However we do encourage people to <a
|
||||
href="mailto:security@mozilla.org?subject=Security%20exploit%20submission">
|
||||
report bugs directly</a> to the Mozilla project, in order to ensure that the
|
||||
bug is made known as soon as possible to the people who can fix it.</p>
|
||||
</dd>
|
||||
|
||||
<dt class="question" id="nondisclosure">If I report the bug
|
||||
directly to you, do I have to keep the bug confidential and not publish
|
||||
information about it in order to receive a reward?</dt>
|
||||
|
||||
<dd class="answer">
|
||||
<p>No. We're rewarding you for finding a bug, not trying to buy
|
||||
your silence. However if you report the bug through the standard
|
||||
Mozilla process and haven't already published information about it then
|
||||
we do ask that you follow the guidelines set forth in the official
|
||||
policy on <a href="{{ url('mozorg.about.governance.policies.security.bugs') }}">handling
|
||||
Mozilla security bugs</a>. Under this policy security-sensitive bug
|
||||
reports in our Bugzilla system may be kept private for a limited period
|
||||
of time to give us a chance to fix the bug before the bug is made
|
||||
public, with an option for the bug reporter (or others) to open the bug
|
||||
to public view earlier whenever circumstances warrant it (e.g., if your
|
||||
bug report is being completely ignored).</p>
|
||||
</dd>
|
||||
|
||||
<dt class="question" id="cooperation">I don't have the time or
|
||||
desire to work with you further in investigating and fixing the bug;
|
||||
can I still get a bug bounty reward?</dt>
|
||||
|
||||
<dd class="answer">
|
||||
<p>Yes. Again, we're rewarding you for finding a bug, not trying to
|
||||
buy your cooperation. However we do invite you to work together with
|
||||
us, and we hope that you'll accept that offer in the spirit in which it
|
||||
was intended. In return you'll get the opportunity to work as a full
|
||||
member of the team fixing your bug and see "from the inside" exactly
|
||||
how Mozilla security bugs get resolved.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</article>
|
||||
{% endblock %}
|
|
@ -0,0 +1,129 @@
|
|||
{% extends "security/base.html" %}
|
||||
|
||||
{% block page_title %}Mozilla Security Bug Bounty Program{% endblock %}
|
||||
|
||||
{% block article %}
|
||||
<article class="section-content" itemscope itemtype="http://schema.org/Article">
|
||||
<header>
|
||||
<h1 itemprop="name">Bug Bounty Program</h1>
|
||||
</header>
|
||||
<div itemprop="articleBody">
|
||||
<h2>Introduction</h2>
|
||||
|
||||
<p>The Mozilla Security Bug Bounty Program is designed to encourage
|
||||
security research in Mozilla software and to reward those who help us create
|
||||
the safest Internet clients in existence.</p>
|
||||
|
||||
<p>Many thanks to <a href="http://en.wikipedia.org/wiki/Linspire">Linspire</a>
|
||||
and <a href="http://www.markshuttleworth.com/">Mark Shuttleworth</a>, who
|
||||
provided start-up funding for this endeavor.</p>
|
||||
|
||||
<h2>General Bounty Guidelines</h2>
|
||||
|
||||
<p>Mozilla will pay a bounty for certain client and service security bugs,
|
||||
as detailed below. All security bugs must follow the following general
|
||||
criteria to be eligible:</p>
|
||||
|
||||
<ul>
|
||||
<li>Security bug must be original and previously unreported.</li>
|
||||
<li>Security bug must be a remote exploit.</li>
|
||||
<li>Submitter must not be the author of the buggy code nor otherwise involved
|
||||
in its
|
||||
contribution to the Mozilla project (such as by providing check-in
|
||||
reviews).
|
||||
</li>
|
||||
<li>Employees of the Mozilla Foundation and its subsidiaries are
|
||||
ineligible.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>If you found the security bug as part of your job (in other words, while
|
||||
being
|
||||
paid to work on Mozilla code) then we would appreciate your not applying
|
||||
for the bounty. Our funds are limited and we would like this program to
|
||||
focus on people who are not otherwise paid to work on the Mozilla project.</p>
|
||||
<p>Mozilla reserves the right to not give a bounty payment if we believe
|
||||
the actions of the reporter have endangered the security of Mozilla's end
|
||||
users.</p>
|
||||
<p>If two or more people report the bug together the
|
||||
reward will be divided among them. </p>
|
||||
|
||||
<h2>Client Reward Guidelines</h2>
|
||||
|
||||
<p>The bounty for valid critical client security bugs will be $3000 (US) cash
|
||||
reward and a Mozilla T-shirt. The bounty will be awarded for
|
||||
<a href="https://wiki.mozilla.org/Security_Severity_Ratings">sec-critical and
|
||||
sec-high</a> severity security bugs that meet the following criteria:</p>
|
||||
|
||||
<ul>
|
||||
<li>Security bug is present in the most recent main development (i.e.,
|
||||
Aurora, Beta or EarlyBird, and nightly mozilla-central releases) or released
|
||||
versions of Firefox, Thunderbird, Firefox for Android, or in Mozilla services
|
||||
which could compromise users of those products, as released by Mozilla
|
||||
Corporation.
|
||||
</li>
|
||||
<li>Security bugs in or caused by additional 3rd-party software (e.g.
|
||||
plugins, extensions) are excluded from the Bug Bounty program.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>More information about this program can be found in the
|
||||
<a href="{{ url('security.bug-bounty-faq') }}">Client Security
|
||||
Bug Bounty Program FAQ.</a>
|
||||
|
||||
<h2>Web Application and Services Reward Guidelines</h2>
|
||||
|
||||
<p>The bounty for valid web applications or services related security bugs,
|
||||
we are giving a range starting at $500 (US) for high severity and, in some
|
||||
cases,
|
||||
may pay up to $3000 (US) for extraordinary or critical vulnerabilities.
|
||||
We will also include a Mozilla T-shirt. The bounty will be awarded for
|
||||
<a
|
||||
href="https://wiki.mozilla.org/WebAppSec/Web_App_Severity_Ratings#
|
||||
Web_Application_Severity_Ratings_Table">
|
||||
sec-critical and sec-high</a> security bugs that meet the following
|
||||
criteria:</p>
|
||||
|
||||
<ul>
|
||||
<li>Security bug is present in the web properties outlined in the
|
||||
<a href="{{ url('security.bug-bounty-faq-webapp') }}">Web Application Security Bounty
|
||||
FAQ.</a></li>
|
||||
<li>Security bug is on the list of sites which part of the bounty.
|
||||
See the <a href="{{ url('security.bug-bounty-faq-webapp') }}#eligible-bugs">eligible bugs</a>
|
||||
section of the <a href="{{ url('security.bug-bounty-faq-webapp') }}">Web Application Security
|
||||
Bounty FAQ</a>
|
||||
for the list of sites which is included under the bounty.</li>
|
||||
</ul>
|
||||
|
||||
<p>More information about this program can be found in the
|
||||
<a href="{{ url('security.bug-bounty-faq-webapp') }}">Web Application Security Bounty
|
||||
FAQ.</a></p>
|
||||
|
||||
<h2>Process</h2>
|
||||
|
||||
<p>Please <a href="https://bugzilla.mozilla.org/enter_bug.cgi">file a bug</a> describing the
|
||||
security bug; be sure to check the box near the bottom of the entry
|
||||
form that marks this bug report as confidential. We encourage you
|
||||
to attach a "proof of concept" testcase or link to the bug report that
|
||||
demonstrates the vulnerability. While not required, such a testcase will
|
||||
help us judge submissions more quickly and accurately.</p>
|
||||
|
||||
<p>Notify the
|
||||
<a
|
||||
href="mailto:security@mozilla.org?subject=Security%20Bug%20Bounty:<DESCRIPTION>">
|
||||
Mozilla Security Group</a> by email and include the number of the bug you filed
|
||||
and a brief summary. If you cannot file a bug include the full details
|
||||
in the email and attach any proof of concept testcases or links.
|
||||
Mozilla Foundation staff and the Mozilla Security Group will consider
|
||||
your submission for the Security Bug Bounty and will contact you.</p>
|
||||
|
||||
<p>We ask that you be available to provide further information on the bug
|
||||
as needed, and invite you to work together with Mozilla engineers in
|
||||
reproducing, diagnosing, and fixing the bug. As part of this process we
|
||||
will provide you full access to participate in our internal discussions
|
||||
about the bug; for more information read our <a
|
||||
href="{{ url('mozorg.about.governance.policies.security.bugs') }}">policy</a>
|
||||
for handling security bugs.</p>
|
||||
</div>
|
||||
</article>
|
||||
{% endblock %}
|
|
@ -0,0 +1,227 @@
|
|||
{# 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/. -#}
|
||||
|
||||
{% extends "security/base.html" %}
|
||||
|
||||
{% block page_title_suffix %}{% endblock %}
|
||||
{% block page_title %}{{ _('Mozilla Security') }}{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}{% endblock %}
|
||||
|
||||
{% block body_id %}security-landing{% endblock %}
|
||||
|
||||
{% block main_feature %}
|
||||
<hgroup id="main-feature" class="center">
|
||||
<h1 class="large">{{ _('Mozilla Security') }}</h1>
|
||||
</hgroup>
|
||||
{% endblock %}
|
||||
|
||||
{% block side_nav %}{% endblock %}
|
||||
|
||||
{% block article %}
|
||||
<article class="section-content" itemscope itemtype="http://schema.org/Article">
|
||||
<header>
|
||||
<h1 itemprop="name">Security Center</h1>
|
||||
</header>
|
||||
<div itemprop="articleBody">
|
||||
<p>Whether you're using the Web or checking your email, you care about
|
||||
your security and privacy. In the Mozilla project
|
||||
<a href="{{ url('security.advisories') }}">we understand the importance of security</a>.
|
||||
Here you will find alerts and announcements on security and privacy
|
||||
issues, general tips for surfing the Web and using email more securely,
|
||||
more information about how we maintain and enhance the security of our
|
||||
products, and useful links for Web developers.</p>
|
||||
|
||||
<p>On this page:</p>
|
||||
|
||||
<ul class="toc">
|
||||
<li><a href="#Security_Updates">Security Updates</a></li>
|
||||
<li><a href="#Tips_for_secure_browsing">Tips for Secure Browsing</a></li>
|
||||
<li><a href="#Tips_for_using_email_securely">Tips for Using Email Securely</a></li>
|
||||
<li><a href="#For_Developers">For Developers: Contacting Mozilla</a>
|
||||
(our <a href="#pgpkey">PGP key</a>)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="Security_Updates">Security Updates</h2>
|
||||
|
||||
<ul>
|
||||
<li><a href="{{ url('security.advisories') }}">
|
||||
Mozilla Foundation Security Advisories</a> for all products
|
||||
</li>
|
||||
<li><a href="{{ url('security.known-vulnerabilities') }}">
|
||||
Known vulnerabilities</a> listed by product
|
||||
</li>
|
||||
<li><a href="http://blog.mozilla.com/">The Mozilla Blog</a> announces
|
||||
all of our releases.
|
||||
</li>
|
||||
<li><a href="http://blog.mozilla.com/security/">The Mozilla Security Blog</a>
|
||||
features security-related articles about Mozilla products.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>The latest security updates will be delivered to most users
|
||||
automatically. Users who have turned off automatic updates can use the
|
||||
"Check for Updates..." item on the Help menu. If the menu item is disabled
|
||||
your account does not have sufficient privileges to update Firefox--contact
|
||||
the person who installed Firefox on your machine. Additional help is also
|
||||
available through our <a href="http://support.mozilla.com/">Community
|
||||
Support</a> site.</p>
|
||||
|
||||
|
||||
<h2 id="Tips_for_secure_browsing">Tips for Secure Browsing</h2>
|
||||
|
||||
<ul>
|
||||
<li>Always use the most current version of your
|
||||
<a href="/firefox/">browser</a>.
|
||||
</li>
|
||||
<li>Check for the "lock" icon on the status bar that shows that you
|
||||
are on a secured web site. Also check that the URL begins with
|
||||
"https" in the location bar when making transactions online.
|
||||
</li>
|
||||
<li>In the Tools menu of Firefox, Tools > Options... > Privacy,
|
||||
you can clear your information with one click of a button. This
|
||||
is especially useful when using a computer in a public location.
|
||||
</li>
|
||||
<li>Perform transactions (like shopping or submitting personal
|
||||
information) at sites that are well established and that are familiar
|
||||
to you. If you're not familiar with a site, make sure that the
|
||||
site has a privacy policy and information about the site's security
|
||||
measures.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="Tips_for_using_email_securely">Tips for Using Email Securely</h2>
|
||||
|
||||
<ul>
|
||||
<li>Be aware that it is extremely easy for someone to forge an email
|
||||
message to make it appear as if the message has been sent by your bank,
|
||||
a software vendor (e.g., Microsoft), or another entity with whom you do
|
||||
business. If a message requests that you send your password or other
|
||||
private information, or asks that you run or install an attached file,
|
||||
then it is very likely that the message is not legitimate. When in
|
||||
doubt, just mark the message as "junk" and delete it.
|
||||
</li>
|
||||
<li>Be cautious when clicking on links sent to you in email messages.
|
||||
If you do click on such a link, double-check the name of the site as
|
||||
shown in the location bar of the browser, and be especially careful if
|
||||
the site name displayed is an IP address (e.g., "192.168.25.75")
|
||||
instead of a domain name (e.g., "www.example.com"); in the former case
|
||||
it is very likely the site is not legitimate. Don't enter any personal
|
||||
information into forms displayed at such a site, and if you have any
|
||||
concerns whatsoever about your security, just close the browser window.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="For_Developers">For Developers: Contacting Mozilla</h2>
|
||||
|
||||
<p>Report security-related bugs and learn more about how we secure our
|
||||
products:</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>If you believe that you've found a Mozilla-related
|
||||
security vulnerability, please report it by sending email to the
|
||||
address security@mozilla.org.</strong> Note that your report may be
|
||||
eligible for a reward; see below.
|
||||
</li>
|
||||
<li>For more information on how to report security vulnerabilities
|
||||
and how the Mozilla community will respond to such reports, see our
|
||||
<a href="{{ url('mozorg.about.governance.policies.security.bugs') }}">policy
|
||||
for handling security bugs</a>.
|
||||
</li>
|
||||
<li>We want to make Firefox, Thunderbird, the Mozilla Suite, and
|
||||
other Mozilla products as secure as possible, and want to encourage
|
||||
research, study, timely disclosure, and rapid fixing of any serious
|
||||
security vulnerabilities. We've established a
|
||||
<a href="{{ url('security.bug-bounty') }}">Security Bug
|
||||
Bounty Program</a> to reward people who help us reach that objective.
|
||||
</li>
|
||||
<li>Mozilla-based products include a default list of CA certificates
|
||||
used when connecting to SSL-enabled servers and in other contexts. If you
|
||||
are a CA and would like your CA certificate(s) considered for inclusion
|
||||
in Mozilla, please see the <a href="{{ url('mozorg.about.governance.policies.security.certs.policy') }}">
|
||||
Mozilla CA certificate policy</a>.
|
||||
</li>
|
||||
<li>We encourage you to learn more about our
|
||||
<a href="/projects/security/">Mozilla security
|
||||
projects</a> and participate in the development of security features
|
||||
and capabilities in our products.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Press Contact: send mail to <em>press</em> at <em>mozilla dot com</em>.</p>
|
||||
|
||||
<p id="pgpkey">The PGP key for security@mozilla.org can be used to send encrypted mail
|
||||
or to verify responses received from that address.</p>
|
||||
|
||||
<pre>
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: GnuPG v1
|
||||
|
||||
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
|
||||
JdatyZglJXgcBKU=
|
||||
=Ia2B
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
</pre>
|
||||
</div>
|
||||
</article>
|
||||
{% endblock %}
|
|
@ -0,0 +1,73 @@
|
|||
{% extends "security/base.html" %}
|
||||
|
||||
{% block page_title %}Known Vulnerabilities in Mozilla Products{% endblock %}
|
||||
|
||||
{% block article %}
|
||||
<article class="section-content" itemscope itemtype="http://schema.org/Article">
|
||||
<header>
|
||||
<h1 itemprop="name">Known Vulnerabilities in Mozilla Products</h1>
|
||||
</header>
|
||||
<div itemprop="articleBody">
|
||||
<p>The links below list security vulnerabilities known to affect
|
||||
particular versions of Mozilla products and instructions on what users
|
||||
can do to protect themselves. The lists will be added to when new security
|
||||
problems are found. For a complete list not sorted by product or version
|
||||
please see the <a href="{{ url('security.advisories') }}">
|
||||
Mozilla Foundation Security Advisories</a>.</p>
|
||||
|
||||
<p>Please read
|
||||
<a href="{{ url('mozorg.about.governance.policies.security.bugs') }}">
|
||||
Mozilla.org's security bug policy</a> for information on how we handle
|
||||
security bugs. If you have found a security problem which is not on this
|
||||
list and has not already been filed as a bug in
|
||||
<a href="https://bugzilla.mozilla.org/query.cgi">Bugzilla</a>,
|
||||
or if you find errors or inconsistencies in this list, please
|
||||
<a href="mailto:security@mozilla.org">mail us</a>.
|
||||
If needed our PGP key can be found on the main
|
||||
<a href="{{ url('security.index') }}#pgpkey">security</a> page.</p>
|
||||
|
||||
<h2>Product Advisories</h2>
|
||||
<ul>
|
||||
<li><a href="{{ url('security.product-advisories', slug='firefox') }}">Firefox</a></li>
|
||||
<li><a href="{{ url('security.product-advisories', slug='firefox-esr') }}">Firefox ESR</a></li>
|
||||
<li><a href="{{ url('security.product-advisories', slug='thunderbird') }}">Thunderbird</a></li>
|
||||
<li><a href="{{ url('security.product-advisories', slug='thunderbird-esr') }}">Thunderbird ESR</a></li>
|
||||
<li><a href="{{ url('security.product-advisories', slug='seamonkey') }}">SeaMonkey</a></li>
|
||||
</ul>
|
||||
|
||||
<h2>Advisories for older products</h2>
|
||||
|
||||
<h3>Firefox</h3>
|
||||
<ul>
|
||||
<li><a href="{{ url('security.product-version-advisories', slug='firefox-3.6') }}">Firefox 3.6</a></li>
|
||||
<li><a href="{{ url('security.product-version-advisories', slug='firefox-3.5') }}">Firefox 3.5</a></li>
|
||||
<li><a href="{{ url('security.product-version-advisories', slug='firefox-3.0') }}">Firefox 3.0</a></li>
|
||||
<li><a href="{{ url('security.product-version-advisories', slug='firefox-2.0') }}">Firefox 2.0</a></li>
|
||||
<li><a href="{{ url('security.product-version-advisories', slug='firefox-1.5') }}">Firefox 1.5</a></li>
|
||||
<li><a href="{{ url('security.product-version-advisories', slug='firefox-1.0') }}">Firefox 1.0</a></li>
|
||||
</ul>
|
||||
|
||||
<h3>Thunderbird</h3>
|
||||
<ul>
|
||||
<li><a href="{{ url('security.product-version-advisories', slug='thunderbird-3.1') }}">Thunderbird 3.1</a></li>
|
||||
<li><a href="{{ url('security.product-version-advisories', slug='thunderbird-3.0') }}">Thunderbird 3.0</a></li>
|
||||
<li><a href="{{ url('security.product-version-advisories', slug='thunderbird-2.0') }}">Thunderbird 2.0</a></li>
|
||||
<li><a href="{{ url('security.product-version-advisories', slug='thunderbird-1.5') }}">Thunderbird 1.5</a></li>
|
||||
<li><a href="{{ url('security.product-version-advisories', slug='thunderbird-1.0') }}">Thunderbird 1.0</a></li>
|
||||
</ul>
|
||||
|
||||
<h3>SeaMonkey</h3>
|
||||
<ul>
|
||||
<li><a href="{{ url('security.product-version-advisories', slug='seamonkey-2.0') }}">SeaMonkey 2.0</a></li>
|
||||
<li><a href="{{ url('security.product-version-advisories', slug='seamonkey-1.1') }}">SeaMonkey 1.1</a></li>
|
||||
<li><a href="{{ url('security.product-version-advisories', slug='seamonkey-1.0') }}">SeaMonkey 1.0</a></li>
|
||||
</ul>
|
||||
|
||||
<h3>Older</h3>
|
||||
<ul>
|
||||
<li><a href="{{ url('security.product-advisories', slug='mozilla-suite') }}">Mozilla Suite 1.7</a></li>
|
||||
<li><a href="{{ url('security.older-vulnerabilities') }}">Older vulnerabilities</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</article>
|
||||
{% endblock %}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,23 @@
|
|||
<p>Impact key:</p>
|
||||
<ul>
|
||||
<li><span class="critical">Critical:</span>
|
||||
Vulnerability can be used to run attacker code and install
|
||||
software, requiring no user interaction beyond normal browsing.
|
||||
</li>
|
||||
<li><span class="high">High:</span>
|
||||
Vulnerability can be used to gather sensitive data
|
||||
from sites in other windows or inject data or code into
|
||||
those sites, requiring no more than normal browsing actions.
|
||||
</li>
|
||||
<li><span class="moderate">Moderate:</span>
|
||||
Vulnerabilities that would otherwise be High or Critical
|
||||
except they only work in uncommon non-default configurations or
|
||||
require the user to perform complicated and/or unlikely steps.
|
||||
</li>
|
||||
<li><span class="low">Low:</span>
|
||||
Minor security vulnerabilities such as Denial of Service
|
||||
attacks, minor data leaks, or spoofs. (Undetectable spoofs of
|
||||
SSL indicia would have "High" impact because those are generally
|
||||
used to steal sensitive data intended for other sites.)
|
||||
</li>
|
||||
</ul>
|
|
@ -0,0 +1,29 @@
|
|||
{% extends "security/base.html" %}
|
||||
|
||||
{% block page_title %}Mozilla Foundation Secuirty Advisories{% endblock %}
|
||||
|
||||
{% block article %}
|
||||
<article class="section-content" itemscope itemtype="http://schema.org/Article">
|
||||
<header>
|
||||
<h1 itemprop="name">Mozilla Foundation Security Advisories</h1>
|
||||
</header>
|
||||
<div itemprop="articleBody">
|
||||
|
||||
{% include "security/partials/impact_key.html" %}
|
||||
|
||||
<ul>
|
||||
{% for version in product_versions %}
|
||||
<li id="{{ version.slug }}">Fixed in {{ version.name }} <a href="#{{ version.slug }}">#</a>
|
||||
<ul>
|
||||
{% for advisory in version.advisories.all() %}
|
||||
<li><a class="{{ advisory.impact_class }}"
|
||||
href="{{ advisory.get_absolute_url() }}">{{ advisory.id }}</a>: {{ advisory.title|safe }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</article>
|
||||
{% endblock %}
|
|
@ -0,0 +1,18 @@
|
|||
# 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/.
|
||||
|
||||
from nose.tools import eq_
|
||||
from bedrock.mozorg.tests import TestCase
|
||||
from bedrock.security.management.commands import update_security_advisories
|
||||
|
||||
|
||||
class TestUpdateSecurityAdvisories(TestCase):
|
||||
def test_fix_product_name(self):
|
||||
"""Should fix SeaMonkey and nothing else."""
|
||||
eq_(update_security_advisories.fix_product_name('Seamonkey 2.2'),
|
||||
'SeaMonkey 2.2')
|
||||
eq_(update_security_advisories.fix_product_name('Firefox 2.2'),
|
||||
'Firefox 2.2')
|
||||
eq_(update_security_advisories.fix_product_name('fredflintstone 2.2'),
|
||||
'fredflintstone 2.2')
|
|
@ -0,0 +1,23 @@
|
|||
# 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/.
|
||||
|
||||
from bedrock.mozorg.tests import TestCase
|
||||
from bedrock.security.models import Product
|
||||
|
||||
|
||||
class TestModels(TestCase):
|
||||
def test_version_ordering(self):
|
||||
pv3 = Product.objects.create(name='Firefox 24.0.1')
|
||||
pv2 = Product.objects.create(name='Firefox 24.0')
|
||||
pv0 = Product.objects.create(name='Fennec 24.0')
|
||||
pv1 = Product.objects.create(name='Firefox 23.0')
|
||||
pvs = [pv2, pv0, pv3, pv1]
|
||||
self.assertListEqual([pv0, pv1, pv2, pv3], sorted(pvs))
|
||||
|
||||
def test_product_version_slug(self):
|
||||
"""Slug should include the version."""
|
||||
pv0 = Product.objects.create(name='Firefox 24.0.1')
|
||||
pv1 = Product.objects.create(name='Firefox ESR 24.2')
|
||||
self.assertEqual(pv0.slug, 'firefox-24.0.1')
|
||||
self.assertEqual(pv1.slug, 'firefox-esr-24.2')
|
|
@ -0,0 +1,54 @@
|
|||
# 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/.
|
||||
from textwrap import dedent
|
||||
from cStringIO import StringIO
|
||||
from nose.tools import eq_
|
||||
|
||||
from bedrock.security.utils import mfsa_id_from_filename, parse_md_front_matter
|
||||
|
||||
|
||||
def test_parse_front_matter():
|
||||
"""Should return front matter and MD separately."""
|
||||
lines = StringIO(dedent("""
|
||||
---
|
||||
dude: abiding
|
||||
walter: angry
|
||||
donny: oblivious
|
||||
---
|
||||
|
||||
Let's go bowling.
|
||||
"""))
|
||||
yaml, md = parse_md_front_matter(lines)
|
||||
eq_(yaml, dedent("""\
|
||||
dude: abiding
|
||||
walter: angry
|
||||
donny: oblivious
|
||||
"""))
|
||||
eq_(md, "\nLet's go bowling.\n")
|
||||
|
||||
|
||||
def test_parse_front_matter_only():
|
||||
"""Should not care about any other --- lines."""
|
||||
lines = StringIO(dedent("""
|
||||
---
|
||||
dude: abiding
|
||||
walter: angry
|
||||
---
|
||||
|
||||
Art
|
||||
---
|
||||
|
||||
Maude's thing.
|
||||
"""))
|
||||
yaml, md = parse_md_front_matter(lines)
|
||||
eq_(yaml, 'dude: abiding\nwalter: angry\n')
|
||||
eq_(md, "\nArt\n---\n\nMaude's thing.\n")
|
||||
|
||||
|
||||
def test_mfsa_id_from_filename():
|
||||
eq_(mfsa_id_from_filename('announce/2014/mfsa2014-01.md'),
|
||||
'2014-01')
|
||||
eq_(mfsa_id_from_filename('announce/2014/mfsa2014-101.md'),
|
||||
'2014-101')
|
||||
assert mfsa_id_from_filename('dude.txt') is None
|
|
@ -0,0 +1,120 @@
|
|||
# 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/.
|
||||
from django.test import RequestFactory
|
||||
|
||||
from mock import patch, Mock
|
||||
from product_details.version_compare import Version
|
||||
|
||||
from bedrock.mozorg.tests import TestCase
|
||||
from bedrock.security.management.commands.update_security_advisories import add_or_update_advisory
|
||||
from bedrock.security.models import Product
|
||||
from bedrock.security.views import ProductView, ProductVersionView, latest_queryset
|
||||
|
||||
|
||||
class TestViews(TestCase):
|
||||
def setUp(self):
|
||||
pvnames = [
|
||||
'Firefox 3.6',
|
||||
'Firefox 4.0',
|
||||
'Firefox 4.0.1',
|
||||
'Firefox 4.2',
|
||||
'Firefox 4.2.3',
|
||||
'Firefox 24.0',
|
||||
]
|
||||
self.pvs = [Product.objects.create(name=pv) for pv in pvnames]
|
||||
|
||||
def test_product_view_min_version(self):
|
||||
"""Should not include versions below minimum."""
|
||||
pview = ProductView()
|
||||
pview.kwargs = {'slug': 'firefox'}
|
||||
with patch.dict(pview.minimum_versions, {'firefox': Version('4.2')}):
|
||||
self.assertListEqual(pview.get_queryset(),
|
||||
[self.pvs[5], self.pvs[4], self.pvs[3]])
|
||||
|
||||
with patch.dict(pview.minimum_versions, {'firefox': Version('22.0')}):
|
||||
self.assertListEqual(pview.get_queryset(), [self.pvs[5]])
|
||||
|
||||
def test_product_version_view_filter_major(self):
|
||||
"""Given a major version should return all minor versions."""
|
||||
pview = ProductVersionView()
|
||||
pview.kwargs = {'slug': 'firefox-4'}
|
||||
self.assertListEqual(pview.get_queryset(),
|
||||
[self.pvs[4], self.pvs[3], self.pvs[2], self.pvs[1]])
|
||||
|
||||
def test_product_version_view_filter_minor(self):
|
||||
"""Given a minor version should return all point versions."""
|
||||
pview = ProductVersionView()
|
||||
pview.kwargs = {'slug': 'firefox-4.2'}
|
||||
self.assertListEqual(pview.get_queryset(), [self.pvs[4], self.pvs[3]])
|
||||
|
||||
|
||||
class TestLastModified(TestCase):
|
||||
def setUp(self):
|
||||
self.next_id = 1
|
||||
self.rf = RequestFactory()
|
||||
|
||||
def new_advisory(self, mfsa_id=None, title='WILMAAAA!', impact='Critical',
|
||||
announced='August 18, 2014', fixed_in=None,
|
||||
html='Wilma finally leaves Fred for a brontosaurus.'):
|
||||
if mfsa_id is None:
|
||||
mfsa_id = '2014-{0:0>2}'.format(self.next_id)
|
||||
self.next_id += 1
|
||||
|
||||
if fixed_in is None:
|
||||
fixed_in = ['Firefox 29', 'Thunderbird 29']
|
||||
|
||||
return add_or_update_advisory({
|
||||
'mfsa_id': mfsa_id,
|
||||
'title': title,
|
||||
'impact': impact,
|
||||
'announced': announced,
|
||||
'fixed_in': fixed_in,
|
||||
}, html)
|
||||
|
||||
def test_latest_queryset_all(self):
|
||||
"""Should return all advisories for the all page."""
|
||||
advisories = [self.new_advisory() for i in range(10)]
|
||||
req = self.rf.get('/')
|
||||
req.resolver_match = Mock()
|
||||
req.resolver_match.url_name = 'security.advisories'
|
||||
qs = latest_queryset(req, {})
|
||||
self.assertListEqual(advisories, list(qs.order_by('year', 'order')))
|
||||
|
||||
def test_latest_queryset_single(self):
|
||||
"""Should return a single advisory based on PK."""
|
||||
advisories = [self.new_advisory() for i in range(10)]
|
||||
req = self.rf.get('/')
|
||||
req.resolver_match = Mock()
|
||||
req.resolver_match.url_name = 'security.advisory'
|
||||
qs = latest_queryset(req, {'pk': '2014-05'})
|
||||
self.assertEqual(advisories[4], qs.get())
|
||||
|
||||
def test_latest_queryset_product(self):
|
||||
"""Should advisories for a single product."""
|
||||
advisories_fx = [self.new_advisory(fixed_in=['Firefox 30.0']) for i in range(5)]
|
||||
for i in range(5):
|
||||
self.new_advisory(fixed_in=['Thunderbird 30.0'])
|
||||
req = self.rf.get('/')
|
||||
req.resolver_match = Mock()
|
||||
req.resolver_match.url_name = 'security.product-advisories'
|
||||
qs = latest_queryset(req, {'slug': 'firefox'})
|
||||
self.assertListEqual(advisories_fx, list(qs.order_by('year', 'order')))
|
||||
|
||||
def test_latest_queryset_product_version(self):
|
||||
"""Should advisories for a single product version."""
|
||||
advisories_30 = [self.new_advisory(fixed_in=['Firefox 30.{0}'.format(i)])
|
||||
for i in range(5)]
|
||||
advisories_29 = [self.new_advisory(fixed_in=['Firefox 29.0.{0}'.format(i)])
|
||||
for i in range(1, 5)]
|
||||
# make sure the one with no point version is included
|
||||
advisories_29.append(self.new_advisory(fixed_in=['Firefox 29.0']))
|
||||
req = self.rf.get('/')
|
||||
req.resolver_match = Mock()
|
||||
req.resolver_match.url_name = 'security.product-version-advisories'
|
||||
qs = latest_queryset(req, {'slug': 'firefox-30'})
|
||||
self.assertListEqual(advisories_30, list(qs.order_by('year', 'order')))
|
||||
qs = latest_queryset(req, {'slug': 'firefox-30.1'})
|
||||
self.assertEqual(advisories_30[1], qs.get())
|
||||
qs = latest_queryset(req, {'slug': 'firefox-29.0'})
|
||||
self.assertListEqual(advisories_29, list(qs.order_by('year', 'order')))
|
|
@ -0,0 +1,37 @@
|
|||
# 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/.
|
||||
|
||||
from django.conf.urls import patterns, url
|
||||
|
||||
from bedrock.mozorg.util import page
|
||||
from bedrock.security.views import (
|
||||
AdvisoriesView,
|
||||
AdvisoryView,
|
||||
OldAdvisoriesListView,
|
||||
OldAdvisoriesView,
|
||||
ProductView,
|
||||
ProductVersionView,
|
||||
)
|
||||
|
||||
|
||||
urlpatterns = patterns('', # noqa
|
||||
page('', 'security/index.html'),
|
||||
page('bug-bounty', 'security/bug-bounty.html'),
|
||||
page('bug-bounty-faq', 'security/bug-bounty-faq.html'),
|
||||
page('bug-bounty-faq-webapp', 'security/bug-bounty-faq-webapp.html'),
|
||||
url(r'^advisories/$',
|
||||
AdvisoriesView.as_view(), name='security.advisories'),
|
||||
url(r'^advisories/mfsa(?P<pk>\d{4}-\d{2,3})/$',
|
||||
AdvisoryView.as_view(), name='security.advisory'),
|
||||
|
||||
page('known-vulnerabilities', 'security/known-vulnerabilities.html'),
|
||||
page('known-vulnerabilities/older-vulnerabilities', 'security/older-vulnerabilities.html'),
|
||||
url(r'^known-vulnerabilities/(?P<slug>[a-z-]+)/$',
|
||||
ProductView.as_view(), name='security.product-advisories'),
|
||||
url(r'^known-vulnerabilities/(?P<slug>[\w-]+-\d{1,3}(\.\d{1,3})?)/$',
|
||||
ProductVersionView.as_view(), name='security.product-version-advisories'),
|
||||
|
||||
url(r'^announce/\d{4}/mfsa(?P<pk>\d{4}-\d{2,3})\.html$', OldAdvisoriesView.as_view()),
|
||||
url(r'^announce/$', OldAdvisoriesListView.as_view()),
|
||||
)
|
|
@ -0,0 +1,82 @@
|
|||
# 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 codecs
|
||||
from functools import wraps
|
||||
import os
|
||||
import re
|
||||
|
||||
import yaml
|
||||
from markdown import markdown
|
||||
|
||||
|
||||
FILENAME_RE = re.compile('mfsa(\d{4}-\d{2,3})\.md$')
|
||||
|
||||
|
||||
def mfsa_id_from_filename(filename):
|
||||
match = FILENAME_RE.search(filename)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def parse_md_front_matter(lines):
|
||||
"""Return the YAML and MD sections.
|
||||
|
||||
:param: lines iterator
|
||||
:return: str YAML, str Markdown
|
||||
"""
|
||||
# fm_count: 0: init, 1: in YAML, 2: in Markdown
|
||||
fm_count = 0
|
||||
yaml_lines = []
|
||||
md_lines = []
|
||||
for line in lines:
|
||||
# first line we care about is FM start
|
||||
if fm_count < 2 and line.strip() == '---':
|
||||
fm_count += 1
|
||||
continue
|
||||
|
||||
if fm_count == 1:
|
||||
yaml_lines.append(line)
|
||||
|
||||
if fm_count == 2:
|
||||
md_lines.append(line)
|
||||
|
||||
if fm_count < 2:
|
||||
raise ValueError('Front Matter not found.')
|
||||
|
||||
return ''.join(yaml_lines), ''.join(md_lines)
|
||||
|
||||
|
||||
def parse_md_file(file_name):
|
||||
"""Return the YAML and MD sections for file_name."""
|
||||
with codecs.open(file_name, encoding='utf8') as fh:
|
||||
yamltext, mdtext = parse_md_front_matter(fh)
|
||||
|
||||
data = yaml.safe_load(yamltext)
|
||||
if 'mfsa_id' not in data:
|
||||
mfsa_id = mfsa_id_from_filename(file_name)
|
||||
if mfsa_id:
|
||||
data['mfsa_id'] = mfsa_id
|
||||
return data, markdown(mdtext)
|
||||
|
||||
|
||||
def chdir(dirname=None):
|
||||
"""Decorator to run a function in a different directory then return."""
|
||||
def decorator(func):
|
||||
|
||||
@wraps(func)
|
||||
def inner(*args, **kwargs):
|
||||
curdir = os.getcwd()
|
||||
try:
|
||||
if dirname is not None:
|
||||
os.chdir(dirname)
|
||||
return func(*args, **kwargs)
|
||||
finally:
|
||||
os.chdir(curdir)
|
||||
|
||||
return inner
|
||||
|
||||
return decorator
|
|
@ -0,0 +1,143 @@
|
|||
# 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/.
|
||||
|
||||
from django.db.models import Q
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.decorators.http import last_modified
|
||||
from django.views.generic import DetailView, ListView, RedirectView
|
||||
|
||||
from funfactory.urlresolvers import reverse
|
||||
from product_details.version_compare import Version
|
||||
|
||||
from bedrock.mozorg.decorators import cache_control_expires
|
||||
from bedrock.security.models import Product, SecurityAdvisory
|
||||
|
||||
|
||||
def latest_queryset(request, kwargs):
|
||||
"""
|
||||
Return a queryset for use as a way to find last-modified date.
|
||||
:param request: the http request object
|
||||
:param kwargs: the URL param args for the request
|
||||
:return: QuerySet
|
||||
"""
|
||||
urlname = request.resolver_match.url_name.split('.')[1]
|
||||
if urlname == 'advisories':
|
||||
return SecurityAdvisory.objects.all()
|
||||
|
||||
if urlname == 'advisory':
|
||||
pk = kwargs.get('pk')
|
||||
return SecurityAdvisory.objects.filter(pk=pk)
|
||||
|
||||
if urlname == 'product-advisories':
|
||||
slug = kwargs.get('slug')
|
||||
# doesn't take minimum versions into account.
|
||||
# don't think that's really a problem as they shouldn't change.
|
||||
return SecurityAdvisory.objects.filter(fixed_in__product_slug=slug)
|
||||
|
||||
if urlname == 'product-version-advisories':
|
||||
slug = kwargs.get('slug')
|
||||
qfilter = Q(fixed_in__slug__startswith=slug + '.')
|
||||
dots = slug.count('.')
|
||||
if dots == 1:
|
||||
# minor version. add exact match.
|
||||
qfilter |= Q(fixed_in__slug__exact=slug)
|
||||
return SecurityAdvisory.objects.filter(qfilter)
|
||||
|
||||
|
||||
def latest_advisory(request, *args, **kwargs):
|
||||
"""
|
||||
Callback function for use with last_modified decorator.
|
||||
:params: request, *args, **kwargs same as sent to view
|
||||
:return: function
|
||||
"""
|
||||
queryset = latest_queryset(request, kwargs)
|
||||
try:
|
||||
latest = queryset.only('last_modified').latest()
|
||||
except SecurityAdvisory.DoesNotExist:
|
||||
return None
|
||||
|
||||
return latest.last_modified
|
||||
|
||||
|
||||
class AdvisoriesView(ListView):
|
||||
template_name = 'security/advisories.html'
|
||||
queryset = SecurityAdvisory.objects.only('id', 'impact', 'title', 'announced')
|
||||
context_object_name = 'advisories'
|
||||
|
||||
@method_decorator(cache_control_expires(0.5))
|
||||
@method_decorator(last_modified(latest_advisory))
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
return super(AdvisoriesView, self).dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
class AdvisoryView(DetailView):
|
||||
model = SecurityAdvisory
|
||||
template_name = 'security/advisory.html'
|
||||
context_object_name = 'advisory'
|
||||
|
||||
@method_decorator(cache_control_expires(0.5))
|
||||
@method_decorator(last_modified(latest_advisory))
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
return super(AdvisoryView, self).dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
class ProductView(ListView):
|
||||
template_name = 'security/product-advisories.html'
|
||||
context_object_name = 'product_versions'
|
||||
allow_empty = False
|
||||
minimum_versions = {
|
||||
'firefox': Version('4.0'),
|
||||
'thunderbird': Version('6.0'),
|
||||
'seamonkey': Version('2.3'),
|
||||
}
|
||||
|
||||
@method_decorator(cache_control_expires(0.5))
|
||||
@method_decorator(last_modified(latest_advisory))
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
return super(ProductView, self).dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
product_slug = self.kwargs.get('slug')
|
||||
versions = Product.objects.filter(product_slug=product_slug)
|
||||
min_version = self.minimum_versions.get(product_slug)
|
||||
if min_version:
|
||||
versions = [vers for vers in versions if vers.version >= min_version]
|
||||
return sorted(versions, reverse=True)
|
||||
|
||||
|
||||
class ProductVersionView(ListView):
|
||||
template_name = 'security/product-advisories.html'
|
||||
context_object_name = 'product_versions'
|
||||
allow_empty = False
|
||||
|
||||
@method_decorator(cache_control_expires(0.5))
|
||||
@method_decorator(last_modified(latest_advisory))
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
return super(ProductVersionView, self).dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
slug = self.kwargs['slug']
|
||||
qfilter = Q(slug__startswith=slug + '.')
|
||||
dots = slug.count('.')
|
||||
if dots == 1:
|
||||
# minor version. add exact match.
|
||||
qfilter |= Q(slug__exact=slug)
|
||||
versions = Product.objects.filter(qfilter)
|
||||
return sorted(versions, reverse=True)
|
||||
|
||||
|
||||
class CachedRedirectView(RedirectView):
|
||||
@method_decorator(cache_control_expires(24 * 30)) # 30 days
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
return super(CachedRedirectView, self).dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
class OldAdvisoriesView(CachedRedirectView):
|
||||
def get_redirect_url(self, **kwargs):
|
||||
return reverse('security.advisory', kwargs=kwargs)
|
||||
|
||||
|
||||
class OldAdvisoriesListView(CachedRedirectView):
|
||||
def get_redirect_url(self, **kwargs):
|
||||
return reverse('security.advisories')
|
|
@ -56,6 +56,7 @@ SUPPORTED_NONLOCALES += [
|
|||
'gameon',
|
||||
'robots.txt',
|
||||
'credits',
|
||||
'security',
|
||||
]
|
||||
|
||||
ALLOWED_HOSTS = [
|
||||
|
@ -350,6 +351,9 @@ MINIFY_BUNDLES = {
|
|||
'privacy': (
|
||||
'css/privacy/privacy.less',
|
||||
),
|
||||
'security': (
|
||||
'css/security/security.less',
|
||||
),
|
||||
'privacy-day': (
|
||||
'css/privacy/privacy-day.less',
|
||||
),
|
||||
|
@ -828,6 +832,7 @@ INSTALLED_APPS = get_apps(exclude=(
|
|||
'%s.tabzilla' % PROJECT_MODULE,
|
||||
'%s.facebookapps' % PROJECT_MODULE,
|
||||
'%s.externalfiles' % PROJECT_MODULE,
|
||||
'%s.security' % PROJECT_MODULE,
|
||||
|
||||
# libs
|
||||
'django_extensions',
|
||||
|
@ -1035,6 +1040,9 @@ RNA = {
|
|||
'VERIFY_SSL_CERT': os.environ.get('VERIFY_SSL_CERT', False),
|
||||
}
|
||||
|
||||
MOFO_SECURITY_ADVISORIES_PATH = path('mofo_security_advisories')
|
||||
MOFO_SECURITY_ADVISORIES_REPO = 'https://github.com/mozilla/foundation-security-advisories.git'
|
||||
|
||||
LOGGING = {
|
||||
'root': {
|
||||
'level': 'WARNING',
|
||||
|
|
|
@ -32,6 +32,7 @@ urlpatterns = patterns(
|
|||
(r'^privacy', include('bedrock.privacy.urls')),
|
||||
(r'^styleguide/', include('bedrock.styleguide.urls')),
|
||||
(r'^tabzilla/', include('bedrock.tabzilla.urls')),
|
||||
(r'^security/', include('bedrock.security.urls')),
|
||||
(r'', include('bedrock.firefox.urls')),
|
||||
(r'', include('bedrock.mozorg.urls')),
|
||||
(r'', include('bedrock.newsletter.urls')),
|
||||
|
|
|
@ -13,6 +13,7 @@ npm install
|
|||
cp bedrock/settings/local.py-dist bedrock/settings/local.py
|
||||
./manage.py update_product_details
|
||||
./manage.py update_externalfiles
|
||||
./manage.py update_security_advisories
|
||||
mysql-ctl start
|
||||
./manage.py syncdb --migrate --noinput
|
||||
./manage.py rnasync
|
||||
|
|
|
@ -47,6 +47,7 @@ def update_assets(ctx):
|
|||
management_cmd(ctx, 'compress_assets')
|
||||
management_cmd(ctx, 'update_product_details')
|
||||
management_cmd(ctx, 'update_externalfiles')
|
||||
management_cmd(ctx, 'update_security_advisories')
|
||||
|
||||
|
||||
@task
|
||||
|
|
|
@ -782,5 +782,9 @@ RewriteRule ^/media/flash/playerWithControls.swf - [F]
|
|||
# Bug 896474
|
||||
RewriteRule ^/(\w{2,3}(?:-\w{2})?/)?about(/?|/.+)$ /b/$1about$2 [PT]
|
||||
|
||||
#Bug 1040970
|
||||
# Bug 1040970
|
||||
RewriteRule ^/mozillacareers$ https://wiki.mozilla.org/People/mozillacareers?utm_medium=redirect&utm_source=mozillacareers-vanity [L,R=301]
|
||||
|
||||
# Bug 1026184
|
||||
# TODO: enable this after testing and porting/archiving all pages
|
||||
#RewriteRule ^/(\w{2,3}(?:-\w{2})?/)?security(/?|/.+)$ /b/$1security$2 [PT]
|
||||
|
|
|
@ -35,6 +35,10 @@ def render(request, template, context=None, **kwargs):
|
|||
if isinstance(template, list):
|
||||
template = template[0]
|
||||
|
||||
# skip l10n if path exempt
|
||||
if template.partition('/')[0] in settings.SUPPORTED_NONLOCALES:
|
||||
return django_render(request, template, context, **kwargs)
|
||||
|
||||
# Every template gets its own .lang file, so figure out what it is
|
||||
# and pass it in the context
|
||||
context['langfile'] = get_lang_path(template)
|
||||
|
|
|
@ -83,6 +83,11 @@ class TestRender(TestCase):
|
|||
self._test(path, template, 'zu', 'zu,fr;q=0.7,en;q=0.3',
|
||||
302, '/fr/firefox/new/')
|
||||
|
||||
@override_settings(SUPPORTED_NONLOCALES=['non-locale'])
|
||||
def test_supported_nonlocales(self):
|
||||
resp = self.client.get('/non-locale/test/')
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
|
||||
class TestGetAcceptLanguages(TestCase):
|
||||
def _test(self, accept_lang, list):
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
SUCCESS
|
|
@ -14,4 +14,5 @@ urlpatterns = patterns('',
|
|||
page('some-lang-files', 'some_lang_files.html'),
|
||||
page('state-of-mozilla', 'state_of_mozilla.html'),
|
||||
page('firefox/new', 'firefox/new.html'),
|
||||
page('non-locale/test', 'non-locale/test.html'),
|
||||
)
|
||||
|
|
|
@ -134,4 +134,4 @@ class TestNoLocale(TestCase):
|
|||
request = Mock(spec=object)
|
||||
# Note: no .locale on request
|
||||
# Should not cause an exception
|
||||
render(request, None)
|
||||
render(request, '500.html')
|
||||
|
|
|
@ -0,0 +1,497 @@
|
|||
// 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 "../sandstone/sandstone-resp.less";
|
||||
|
||||
// IMPACT LEVELS
|
||||
.critical {
|
||||
background-color: #FF8080;
|
||||
}
|
||||
.high {
|
||||
background-color: #FFC080;
|
||||
}
|
||||
.moderate {
|
||||
background-color: #FFFF80;
|
||||
}
|
||||
// none for .low
|
||||
|
||||
li:target {
|
||||
background-color: lighten(@linkSkyBlue, 25%);
|
||||
}
|
||||
|
||||
#outer-wrapper {
|
||||
overflow: hidden; /* Fix the layout on IE */
|
||||
}
|
||||
|
||||
#masthead {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#main-content {
|
||||
margin-top: 50px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.main-column {
|
||||
float: left;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: (@gridColumnWidth * 9) + (@gridGutterWidth * 9);
|
||||
|
||||
section, article {
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
article {
|
||||
header {
|
||||
.logo {
|
||||
position: relative;
|
||||
float: right;
|
||||
margin: -80px -20px 0 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
p {
|
||||
clear: both;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.meta {
|
||||
color: @textColorTertiary;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
& + * {
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
section {
|
||||
margin-top: 2.5em;
|
||||
padding: 0;
|
||||
|
||||
section {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.highlight {
|
||||
margin: 15px 0;
|
||||
padding: 15px;
|
||||
color: @textColorPrimary;
|
||||
background-color: #f9fafb;
|
||||
box-shadow: 1px 1px 2px #999;
|
||||
line-height: 1.5;
|
||||
|
||||
h2, h3, h4 {
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 20px auto;
|
||||
border-bottom: 1px solid #ccc;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
[tabindex] {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
[role="tab"] {
|
||||
&[aria-expaned] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
a {
|
||||
margin: 0 .5em;
|
||||
}
|
||||
}
|
||||
|
||||
[aria-hidden="true"] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
float: left;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: (@gridColumnWidth * 3) + (@gridGutterWidth * 5);
|
||||
|
||||
section, nav {
|
||||
padding: 25px 25px 0;
|
||||
}
|
||||
|
||||
section section {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
nav h2 {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.section-content {
|
||||
margin-bottom: @gridGutterWidth;
|
||||
padding: @gridGutterWidth;
|
||||
background-color: #fff;
|
||||
@shadow: 0 0 0 1px #fff inset;
|
||||
.box-shadow(@shadow);
|
||||
|
||||
p, ul {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: .5em 0 0 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
#side-principles a:after {
|
||||
content: "\00A0\00BB"; /* nbsp raquo */
|
||||
}
|
||||
|
||||
#side-notices nav {
|
||||
padding: 0;
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 5px 0;
|
||||
font-size: @baseFontSize;
|
||||
.open-sans;
|
||||
|
||||
&:last-of-type {
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
padding: 12px 0 12px 48px;
|
||||
background-image: url(/media/img/privacy/icons.png);
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
&.policy-firefox a,
|
||||
&.policy-firefox-os a,
|
||||
&.policy-firefox-cloud a {
|
||||
background-position: left -40px;
|
||||
}
|
||||
|
||||
&.policy-marketplace a {
|
||||
background-position: left -80px;
|
||||
}
|
||||
|
||||
&.policy-persona a {
|
||||
background-position: left -120px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#side-archives h2 a {
|
||||
.open-sans;
|
||||
font-size: @baseFontSize;
|
||||
letter-spacing: normal;
|
||||
}
|
||||
|
||||
|
||||
/* Landing Page */
|
||||
|
||||
#privacy-landing {
|
||||
#main-content {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
#contact {
|
||||
header {
|
||||
float: left;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
form {
|
||||
display: block;
|
||||
margin-left: 275px;
|
||||
|
||||
legend {
|
||||
font-size: 20px;
|
||||
line-height: 1.3;
|
||||
|
||||
/* A hack for IEs */
|
||||
&, & span {
|
||||
display: block;
|
||||
width: 95%;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
.border-box;
|
||||
margin-bottom: 15px;
|
||||
width: 100%;
|
||||
*width: 95%; /* IE6, IE7 */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Principles Page */
|
||||
|
||||
#privacy-principles {
|
||||
.main-column {
|
||||
margin: 0;
|
||||
|
||||
li {
|
||||
/* Style the list counters */
|
||||
color: @textColorSecondary;
|
||||
font-size: 32px;
|
||||
.open-sans-light;
|
||||
}
|
||||
|
||||
p {
|
||||
/* And reset the style for the child node */
|
||||
color: @textColorPrimary;
|
||||
font-size: @baseFontSize;
|
||||
.open-sans;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Firefox Browser Privacy Notice Page */
|
||||
|
||||
#firefox-notice {
|
||||
.main-column .highlight h3 {
|
||||
margin-top: @baseLine;
|
||||
}
|
||||
}
|
||||
|
||||
/* I18N */
|
||||
|
||||
html[dir="rtl"] {
|
||||
.main-column,
|
||||
.sidebar {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.main-column article header .logo {
|
||||
float: left;
|
||||
margin: -80px 0 0 -20px;
|
||||
}
|
||||
|
||||
.section-content li {
|
||||
margin: .5em 1.5em 0 0;
|
||||
}
|
||||
|
||||
.title-shadow-box {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
#side-notices nav li {
|
||||
a {
|
||||
padding: 12px 48px 12px 0;
|
||||
}
|
||||
|
||||
&.policy-websites a {
|
||||
background-position: right 0;
|
||||
}
|
||||
|
||||
&.policy-firefox a,
|
||||
&.policy-firefox-os a {
|
||||
background-position: right -40px;
|
||||
}
|
||||
|
||||
&.policy-marketplace a {
|
||||
background-position: right -80px;
|
||||
}
|
||||
|
||||
&.policy-persona a {
|
||||
background-position: right -120px;
|
||||
}
|
||||
}
|
||||
|
||||
#side-archives li {
|
||||
margin: 0 20px 0 0;
|
||||
}
|
||||
|
||||
#privacy-landing #contact {
|
||||
header {
|
||||
float: right;
|
||||
}
|
||||
|
||||
form {
|
||||
margin: 0 275px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
#privacy-principles .main-column li {
|
||||
float: right;
|
||||
margin: 30px 65px 0 30px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tablet & Mobile */
|
||||
|
||||
@media only screen and (max-width: @breakDesktop) {
|
||||
#privacy-landing #contact {
|
||||
header {
|
||||
float: none;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
form {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
html[dir="rtl"] {
|
||||
#privacy-landing #contact {
|
||||
header {
|
||||
float: none;
|
||||
}
|
||||
|
||||
form {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: @breakTablet) and (max-width: @breakDesktop) {
|
||||
#main-content {
|
||||
width: @breakTablet;
|
||||
}
|
||||
|
||||
.main-column {
|
||||
.span_narrow(8);
|
||||
margin: 0;
|
||||
width: (@gridColumnWidthNarrow * 8) + (@gridGutterWidth * 8);
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
.span_narrow(4);
|
||||
margin: 0;
|
||||
width: (@gridColumnWidthNarrow * 4) + (@gridGutterWidth * 6);
|
||||
}
|
||||
|
||||
#privacy-principles .main-column li {
|
||||
.span_narrow(5);
|
||||
margin: 30px 10px 0 65px;
|
||||
}
|
||||
|
||||
html[dir="rtl"] #privacy-principles .main-column li {
|
||||
margin: 30px 65px 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: @breakTablet) {
|
||||
#masthead {
|
||||
h2, .breadcrumbs {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#main-content {
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.main-column,
|
||||
.sidebar {
|
||||
.span-all();
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.main-column article header {
|
||||
.logo {
|
||||
float: left;
|
||||
margin: -10px 0 0 -10px;
|
||||
width: 80px;
|
||||
|
||||
& + h1,
|
||||
& + h1 + .meta {
|
||||
margin: 0 0 12px 80px;
|
||||
}
|
||||
}
|
||||
|
||||
.title-shadow-box {
|
||||
margin-top: -50px;
|
||||
}
|
||||
}
|
||||
|
||||
.main-column article .highlight {
|
||||
margin: 15px -15px;
|
||||
}
|
||||
|
||||
#privacy-principles .main-column {
|
||||
header {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
li {
|
||||
float: none;
|
||||
margin: 30px 0 0 40px;
|
||||
width: auto;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: @baseFontSize;
|
||||
}
|
||||
}
|
||||
|
||||
html[dir="rtl"] {
|
||||
.main-column article header {
|
||||
.logo {
|
||||
float: right;
|
||||
margin: -10px -10px 0 0;
|
||||
|
||||
& + h1,
|
||||
& + h1 + .meta {
|
||||
margin: 0 80px 12px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#privacy-principles .main-column li {
|
||||
margin: 30px 40px 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: @breakMobileLandscape) and (max-width: @breakTablet) {
|
||||
#wrapper {
|
||||
width: @breakMobileLandscape - @gridGutterWidth;
|
||||
}
|
||||
|
||||
#masthead {
|
||||
width: @breakMobileLandscape - (@gridGutterWidth * 3);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: @breakMobileLandscape) {
|
||||
/* Bug 980659: Avoid a title overflow in Germany */
|
||||
html[lang="de"] #fxos-notice {
|
||||
.main-column article header {
|
||||
.logo {
|
||||
float: none;
|
||||
|
||||
& + h1,
|
||||
& + h1 + .meta {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче