예제 #1
0
def generate_csrf_token(length=64, valid_seconds=3600, html=False):
    """Generate a CSRF token."""
    now = utils.utcnow()
    valid_token = None

    # Clean up expired tokens to prevent junk from building up in the datastore.
    tokens = data_types.CSRFToken.query(
        data_types.CSRFToken.user_email == helpers.get_user_email())
    tokens_to_delete = []
    for token in tokens:
        if token.expiration_time > now:
            valid_token = token
            continue
        tokens_to_delete.append(token.key)
    ndb_utils.delete_multi(tokens_to_delete)

    # Generate a new token.
    if not valid_token:
        valid_token = data_types.CSRFToken()
        valid_token.value = base64.b64encode(os.urandom(length))
        valid_token.expiration_time = (
            now + datetime.timedelta(seconds=valid_seconds))
        valid_token.user_email = helpers.get_user_email()
        valid_token.put()

    value = valid_token.value
    if html:
        return '<input type="hidden" name="csrf_token" value="%s" />' % value
    return value
예제 #2
0
    def render(self, path, values=None, status=200):
        """Write HTML response."""
        if values is None:
            values = {}

        values['menu_items'] = _MENU_ITEMS
        values['is_oss_fuzz'] = utils.is_oss_fuzz()
        values['is_development'] = (
            environment.is_running_on_app_engine_development())
        values['is_logged_in'] = bool(helpers.get_user_email())

        # Only track analytics for non-admin users.
        values['ga_tracking_id'] = (
            local_config.GAEConfig().get('ga_tracking_id')
            if not auth.is_current_user_admin() else None)

        if values['is_logged_in']:
            values['switch_account_url'] = make_login_url(self.request.url)
            values['logout_url'] = make_logout_url(dest_url=self.request.url)

        template = _JINJA_ENVIRONMENT.get_template(path)

        self._add_security_response_headers()
        self.response.headers['Content-Type'] = 'text/html'
        self.response.out.write(template.render(values))
        self.response.set_status(status)
예제 #3
0
    def post(self):
        """Handles a post request."""
        if not helpers.get_user_email():
            raise helpers.AccessDeniedException()

        result, _ = get_result(self)
        self.render_json(result)
예제 #4
0
  def handle_exception(self, exception):
    """Catch exception and format it properly."""
    try:
      status = 500
      values = {
          'message': str(exception),
          'email': helpers.get_user_email(),
          'traceDump': traceback.format_exc(),
          'status': status,
          'type': exception.__class__.__name__
      }
      if isinstance(exception, helpers.EarlyExitException):
        status = exception.status
        values = exception.to_dict()

      # 4XX is not our fault. Therefore, we hide the trace dump and log on
      # the INFO level.
      if 400 <= status <= 499:
        logging.info(json.dumps(values, cls=JsonEncoder))
        del values['traceDump']
      else:  # Other error codes should be logged with the EXCEPTION level.
        logging.exception(exception)

      if self.should_render_json():
        return self.render_json(values, status)
      if status in (403, 401):
        return self.render_forbidden(str(exception))
      return self.render('error.html', values, status)
    except Exception:
      self.handle_exception_exception()
예제 #5
0
파일: redo.py 프로젝트: zzdxxd/clusterfuzz
    def post(self, testcase):
        """Queue redo tasks."""
        testcase_tasks = self.request.get('tasks')
        user_email = helpers.get_user_email()

        self.redo(testcase, testcase_tasks, user_email)
        self.render_json(show.get_testcase_detail(testcase))
예제 #6
0
  def get(self):
    """Handle a GET request."""
    # Create a list of externally contributed fuzzers.
    user_email = helpers.get_user_email()

    if access.has_access():
      # User is an internal user of ClusterFuzz (eg: ClusterFuzz developer).
      fuzzers_list = (
          data_handler.get_all_fuzzer_names_including_children(
              include_parents=True))
      jobs_list = data_handler.get_all_job_type_names()
    else:
      # User is an external user of ClusterFuzz (eg: non-Chrome dev who
      # submitted a fuzzer or someone with a project in OSS-Fuzz).
      fuzzers_list = external_users.allowed_fuzzers_for_user(
          user_email, include_from_jobs=True, include_parents=True)

      if not fuzzers_list:
        # User doesn't actually have access to any fuzzers.
        raise helpers.AccessDeniedException()

      jobs_list = external_users.allowed_jobs_for_user(user_email)

    fuzzers_list.sort()
    jobs_list.sort()
    result = {
        'info': {
            'fuzzers': fuzzers_list,
            'jobs': jobs_list,
        }
    }

    self.render('fuzzer-stats.html', result)
예제 #7
0
def can_user_access_testcase(testcase):
  """Checks if the current user can access the testcase."""
  if has_access(
      fuzzer_name=testcase.fuzzer_name,
      job_type=testcase.job_type,
      need_privileged_access=testcase.security_flag):
    return True

  user_email = helpers.get_user_email()
  if testcase.uploader_email and testcase.uploader_email == user_email:
    return True

  # Allow owners of bugs to see associated test cases and test case groups.
  issue_id = testcase.bug_information or testcase.group_bug_information
  if not issue_id:
    return False

  itm = issue_tracker_utils.get_issue_tracker_manager(testcase)
  issue = itm.get_issue(int(issue_id))
  if not issue:
    return False

  config = db_config.get()
  if config.relax_testcase_restrictions or _is_domain_allowed(user_email):
    return (any(utils.emails_equal(user_email, cc) for cc in issue.cc) or
            utils.emails_equal(user_email, issue.owner) or
            utils.emails_equal(user_email, issue.reporter))

  return utils.emails_equal(user_email, issue.owner)
예제 #8
0
  def render_forbidden(self, message):
    """Write HTML response for 403."""
    login_url = make_login_url(dest_url=request.url)
    user_email = helpers.get_user_email()
    if not user_email:
      return self.redirect(login_url)

    contact_string = db_config.get_value('contact_string')
    template_values = {
        'message': message,
        'user_email': helpers.get_user_email(),
        'login_url': login_url,
        'switch_account_url': login_url,
        'logout_url': make_logout_url(dest_url=request.url),
        'contact_string': contact_string,
    }
    return self.render('error-403.html', template_values, 403)
예제 #9
0
def get_result(this):
    """Get the result."""
    params = {k: v for k, v in this.request.iterparams()}
    page = helpers.cast(
        this.request.get('page') or 1, int, "'page' is not an int.")

    query = datastore_query.Query(data_types.TestcaseUploadMetadata)
    query.order('timestamp', is_desc=True)

    if not access.has_access(need_privileged_access=True):
        query.filter('uploader_email', helpers.get_user_email())
        params['permission'] = {'uploaderEmail': helpers.get_user_email()}

    entities, total_pages, total_items, has_more = query.fetch_page(
        page=page, page_size=PAGE_SIZE, projection=None, more_limit=MORE_LIMIT)

    items = []
    for entity in entities:
        items.append({
            'timestamp':
            utils.utc_datetime_to_timestamp(entity.timestamp),
            'testcaseId':
            entity.testcase_id,
            'uploaderEmail':
            entity.uploader_email,
            'filename':
            entity.filename,
            'bundled':
            entity.bundled,
            'pathInArchive':
            entity.path_in_archive,
            'status':
            entity.status
        })

    attach_testcases(items)

    result = {
        'hasMore': has_more,
        'items': items,
        'page': page,
        'pageSize': PAGE_SIZE,
        'totalItems': total_items,
        'totalPages': total_pages,
    }
    return result, params
예제 #10
0
def get_result(this):
    """Get the result."""
    params = {k: v for k, v in this.request.iterparams()}
    page = helpers.cast(
        this.request.get("page") or 1, int, "'page' is not an int.")

    query = datastore_query.Query(data_types.TestcaseUploadMetadata)
    query.order("timestamp", is_desc=True)

    if not access.has_access(need_privileged_access=True):
        query.filter("uploader_email", helpers.get_user_email())
        params["permission"] = {"uploaderEmail": helpers.get_user_email()}

    entities, total_pages, total_items, has_more = query.fetch_page(
        page=page, page_size=PAGE_SIZE, projection=None, more_limit=MORE_LIMIT)

    items = []
    for entity in entities:
        items.append({
            "timestamp":
            utils.utc_datetime_to_timestamp(entity.timestamp),
            "testcaseId":
            entity.testcase_id,
            "uploaderEmail":
            entity.uploader_email,
            "filename":
            entity.filename,
            "bundled":
            entity.bundled,
            "pathInArchive":
            entity.path_in_archive,
            "status":
            entity.status,
        })

    attach_testcases(items)

    result = {
        "hasMore": has_more,
        "items": items,
        "page": page,
        "pageSize": PAGE_SIZE,
        "totalItems": total_items,
        "totalPages": total_pages,
    }
    return result, params
예제 #11
0
    def get(self):
        """Handles get request."""
        email = helpers.get_user_email()
        if not email:
            raise helpers.AccessDeniedException()

        is_privileged_or_domain_user = access.has_access(
            need_privileged_access=False)
        if is_privileged_or_domain_user or _is_uploader_allowed(email):
            # Privileged, domain and upload users can see all job and fuzzer names.
            allowed_jobs = data_handler.get_all_job_type_names()
            allowed_fuzzers = data_handler.get_all_fuzzer_names_including_children(
                include_parents=True)
        else:
            # Check if this is an external user with access to certain fuzzers/jobs.
            allowed_jobs = external_users.allowed_jobs_for_user(email)
            allowed_fuzzers = external_users.allowed_fuzzers_for_user(
                email, include_from_jobs=True)

            if not allowed_fuzzers and not allowed_jobs:
                raise helpers.AccessDeniedException()

        has_issue_tracker = bool(data_handler.get_issue_tracker_name())

        result, params = get_result()
        return self.render(
            'upload.html', {
                'fieldValues': {
                    'blackboxFuzzers':
                    filter_blackbox_fuzzers(allowed_fuzzers),
                    'jobs':
                    allowed_jobs,
                    'libfuzzerTargets':
                    filter_target_names(allowed_fuzzers, 'libFuzzer'),
                    'aflTargets':
                    filter_target_names(allowed_fuzzers, 'afl'),
                    'honggfuzzTargets':
                    filter_target_names(allowed_fuzzers, 'honggfuzz'),
                    'isChromium':
                    utils.is_chromium(),
                    'sandboxedJobs':
                    data_types.INTERNAL_SANDBOXED_JOB_TYPES,
                    'csrfToken':
                    form.generate_csrf_token(),
                    'isExternalUser':
                    not is_privileged_or_domain_user,
                    'uploadInfo':
                    gcs.prepare_blob_upload()._asdict(),
                    'hasIssueTracker':
                    has_issue_tracker,
                },
                'params': params,
                'result': result
            })
예제 #12
0
def get_permission_names(entity_kind):
    """Get scoped fuzzer names."""
    # pylint: disable=protected-access
    permissions = external_users._get_permissions_query_for_user(
        helpers.get_user_email(), entity_kind)

    names = []
    for permission in permissions:
        suffix = '*' if permission.is_prefix else ''
        names.append(permission.entity_name + suffix)
    return names
예제 #13
0
 def render_forbidden(self, message):
   """Write HTML response for 403."""
   contact_string = db_config.get_value('contact_string')
   template_values = {
       'message': message,
       'user_email': helpers.get_user_email(),
       'login_url': users.create_login_url(dest_url=self.request.url),
       'switch_account_url': make_switch_account_url(self.request.url),
       'logout_url': users.create_logout_url(dest_url=self.request.url),
       'contact_string': contact_string,
   }
   self.render('error-403.html', template_values, 403)
예제 #14
0
    def get(self):
        """Handles get request."""
        email = helpers.get_user_email()
        if not email:
            raise helpers.AccessDeniedException()

        is_privileged_or_domain_user = access.has_access(
            need_privileged_access=False)
        if is_privileged_or_domain_user or _is_uploader_allowed(email):
            # Privileged, domain and upload users can see all job and fuzzer names.
            allowed_jobs = data_handler.get_all_job_type_names()
            allowed_fuzzers = data_handler.get_all_fuzzer_names_including_children(
                include_parents=True)
        else:
            # Check if this is an external user with access to certain fuzzers/jobs.
            allowed_jobs = external_users.allowed_jobs_for_user(email)
            allowed_fuzzers = external_users.allowed_fuzzers_for_user(
                email, include_from_jobs=True)

            if not allowed_fuzzers and not allowed_jobs:
                raise helpers.AccessDeniedException()

        has_issue_tracker = bool(data_handler.get_issue_tracker_name())

        result, params = get_result(self)
        self.render(
            "upload.html",
            {
                "fieldValues": {
                    "jobs":
                    allowed_jobs,
                    "libfuzzerTargets":
                    filter_target_names(allowed_fuzzers, "libFuzzer"),
                    "aflTargets":
                    filter_target_names(allowed_fuzzers, "afl"),
                    "isChromium":
                    utils.is_chromium(),
                    "sandboxedJobs":
                    data_types.INTERNAL_SANDBOXED_JOB_TYPES,
                    "csrfToken":
                    form.generate_csrf_token(),
                    "isExternalUser":
                    not is_privileged_or_domain_user,
                    "uploadInfo":
                    gcs.prepare_blob_upload()._asdict(),
                    "hasIssueTracker":
                    has_issue_tracker,
                },
                "params": params,
                "result": result,
            },
        )
예제 #15
0
    def get(self, extra=None):
        """Handle a GET request."""
        if not access.has_access():
            # User is an external user of ClusterFuzz (eg: non-Chrome dev who
            # submitted a fuzzer or someone with a project in OSS-Fuzz).
            user_email = helpers.get_user_email()
            fuzzers_list = external_users.allowed_fuzzers_for_user(
                user_email, include_from_jobs=True, include_parents=True)
            if not fuzzers_list:
                # User doesn't actually have access to any fuzzers.
                raise helpers.AccessDeniedException(
                    "You don't have access to any fuzzers.")

        return self.render('fuzzer-stats.html', {})
예제 #16
0
def get_user_job_type():
  """Return the job_type that is assigned to the current user. None means one
    can access any job type. You might want to invoke get_access(..) with
    the job type afterward."""
  email = helpers.get_user_email()
  privileged_user_emails = (db_config.get_value('privileged_users') or
                            '').splitlines()
  for privileged_user_email in privileged_user_emails:
    if ';' in privileged_user_email:
      tokens = privileged_user_email.split(';')
      privileged_user_real_email = tokens[0]
      privileged_user_job_type = tokens[1]
      if utils.emails_equal(email, privileged_user_real_email):
        return privileged_user_job_type
  return None
예제 #17
0
def can_user_access_testcase(testcase):
  """Checks if the current user can access the testcase."""
  config = db_config.get()
  need_privileged_access = (
      testcase.security_flag and not config.relax_security_bug_restrictions)

  if has_access(
      fuzzer_name=testcase.fuzzer_name,
      job_type=testcase.job_type,
      need_privileged_access=need_privileged_access):
    return True

  user_email = helpers.get_user_email()
  if testcase.uploader_email and testcase.uploader_email == user_email:
    return True

  # Allow owners of bugs to see associated test cases and test case groups.
  issue_id = testcase.bug_information or testcase.group_bug_information
  if not issue_id:
    return False

  itm = issue_tracker_utils.get_issue_tracker_manager(testcase)
  issue_id = int(issue_id)
  associated_issue = itm.get_issue(issue_id)
  if not associated_issue:
    return False

  # Look at both associated issue and original issue (if the associated one
  # is a duplicate of the original issue).
  issues_to_check = [associated_issue]
  if associated_issue.merged_into:
    original_issue = itm.get_original_issue(issue_id)
    if original_issue:
      issues_to_check.append(original_issue)

  relaxed_restrictions = (
      config.relax_testcase_restrictions or _is_domain_allowed(user_email))
  for issue in issues_to_check:
    if relaxed_restrictions:
      if (any(utils.emails_equal(user_email, cc) for cc in issue.cc) or
          utils.emails_equal(user_email, issue.owner) or
          utils.emails_equal(user_email, issue.reporter)):
        return True

    elif utils.emails_equal(user_email, issue.owner):
      return True

  return False
예제 #18
0
def check_access_and_get_testcase(testcase_id):
  """Check the failed attempt count and get the testcase."""
  if not helpers.get_user_email():
    raise helpers.UnauthorizedException()

  if not testcase_id:
    raise helpers.EarlyExitException('No test case specified!', 404)

  try:
    testcase = data_handler.get_testcase_by_id(testcase_id)
  except errors.InvalidTestcaseError:
    raise helpers.EarlyExitException('Invalid test case!', 404)

  if not can_user_access_testcase(testcase):
    raise helpers.AccessDeniedException()

  return testcase
예제 #19
0
    def _check_user_access_and_get_job_filter(self, fuzzer, job):
        """Check whether the current user has access to stats for the fuzzer or job.
    Returns a job filter that should be applied to the query."""
        access_by_fuzzer_or_job = access.has_access(fuzzer_name=fuzzer,
                                                    job_type=job)
        if access_by_fuzzer_or_job:
            # User has full access to the fuzzer, or the specified job.
            # None means no filters => all jobs.
            return _get_filter_from_job(job)

        if not job:
            # Job not specified and user doesn't have full access to the fuzzer. Check
            # if the user has any allowed jobs and use that as a filter.
            allowed_jobs = external_users.allowed_jobs_for_user(
                helpers.get_user_email())
            if allowed_jobs:
                return allowed_jobs

        raise helpers.AccessDeniedException()
예제 #20
0
    def get(self):
        """Handle a GET request."""
        project = request.get('project')

        if access.has_access():
            # User is an internal user of ClusterFuzz (eg: ClusterFuzz developer).

            # Show all projects in the list, since this allows user to pick another
            # project as needed.
            projects_list = data_handler.get_all_project_names()

            # Filter fuzzers and job list if a project is provided.
            fuzzers_list = (
                data_handler.get_all_fuzzer_names_including_children(
                    include_parents=True, project=project))
            jobs_list = data_handler.get_all_job_type_names(project=project)
        else:
            # User is an external user of ClusterFuzz (eg: non-Chrome dev who
            # submitted a fuzzer or someone with a project in OSS-Fuzz).
            user_email = helpers.get_user_email()

            # TODO(aarya): Filter fuzzer and job if |project| is provided.
            fuzzers_list = sorted(
                external_users.allowed_fuzzers_for_user(user_email,
                                                        include_from_jobs=True,
                                                        include_parents=True))
            if not fuzzers_list:
                # User doesn't actually have access to any fuzzers.
                raise helpers.AccessDeniedException(
                    "You don't have access to any fuzzers.")

            jobs_list = sorted(
                external_users.allowed_jobs_for_user(user_email))
            projects_list = sorted(
                {data_handler.get_project_name(job)
                 for job in jobs_list})

        result = {
            'projects': projects_list,
            'fuzzers': fuzzers_list,
            'jobs': jobs_list,
        }
        return self.render_json(result)
예제 #21
0
def get_scope():
    """Get the scope object for the user."""
    user_email = helpers.get_user_email()
    is_privileged = access.has_access(need_privileged_access=True)
    everything = (is_privileged or access.has_access())

    # pylint: disable=protected-access
    job_types = external_users._allowed_entities_for_user(
        user_email, data_types.PermissionEntityKind.JOB)

    allowed_job_type = access.get_user_job_type()
    if allowed_job_type:
        job_types.append(allowed_job_type)

    # pylint: disable=protected-access
    fuzzer_names = external_users._allowed_entities_for_user(
        user_email, data_types.PermissionEntityKind.FUZZER)

    return Scope(everything, is_privileged, job_types, fuzzer_names,
                 allowed_job_type)
예제 #22
0
    def update_issue(testcase, issue_id, needs_summary_update):
        """Associate (or update) an existing issue with the testcase."""
        issue_id = helpers.cast(issue_id, int,
                                'Issue ID (%s) is not a number!' % issue_id)
        itm = helpers.get_issue_tracker_manager(testcase)

        issue = helpers.get_or_exit(
            lambda: itm.get_issue(issue_id),
            'Issue (id=%d) is not found!' % issue_id,
            'Failed to get the issue (id=%s).' % issue_id, Exception)

        if not issue.open:
            raise helpers.EarlyExitException(
                ('The issue (%d) is already closed and further updates are not'
                 ' allowed. Please file a new issue instead!') % issue_id, 400)

        # Create issue parameters.
        issue.comment = data_handler.get_issue_description(
            testcase, helpers.get_user_email())
        issue_summary = data_handler.get_issue_summary(testcase)

        # NULL states leads to unhelpful summaries, so do not update in that case.
        if needs_summary_update and testcase.crash_state != 'NULL':
            issue.summary = issue_summary

        # Add label on memory tool used.
        issue_filer.add_memory_tool_label_if_needed(issue, testcase)

        # Add view restrictions for internal job types.
        issue_filer.add_view_restrictions_if_needed(issue, testcase)

        # Don't enforce security severity label on an existing issue.

        itm.save(issue)

        testcase.bug_information = str(issue_id)
        testcase.put()

        data_handler.update_group_bug(testcase.group_id)

        helpers.log('Updated issue %sd' % issue_id, helpers.MODIFY_OPERATION)
예제 #23
0
    def update_issue(testcase, issue_id, needs_summary_update):
        """Associate (or update) an existing issue with the testcase."""
        issue_id = helpers.cast(issue_id, int,
                                'Issue ID (%s) is not a number!' % issue_id)
        issue_tracker = helpers.get_issue_tracker_for_testcase(testcase)

        issue = helpers.get_or_exit(
            lambda: issue_tracker.get_issue(issue_id),
            'Issue (id=%d) is not found!' % issue_id,
            'Failed to get the issue (id=%s).' % issue_id, Exception)

        if not issue.is_open:
            raise helpers.EarlyExitException(
                ('The issue (%d) is already closed and further updates are not'
                 ' allowed. Please file a new issue instead!') % issue_id, 400)

        if not testcase.is_crash():
            raise helpers.EarlyExitException(
                'This is not a crash testcase, so issue update is not applicable.',
                400)

        issue_comment = data_handler.get_issue_description(
            testcase, helpers.get_user_email())
        if needs_summary_update:
            issue.title = data_handler.get_issue_summary(testcase)

        policy = issue_tracker_policy.get(issue_tracker.project)
        properties = policy.get_existing_issue_properties()
        for label in properties.labels:
            for result in issue_filer.apply_substitutions(
                    policy, label, testcase):
                issue.labels.add(result)

        issue.save(new_comment=issue_comment)

        testcase.bug_information = str(issue_id)
        testcase.put()

        data_handler.update_group_bug(testcase.group_id)

        helpers.log('Updated issue %sd' % issue_id, helpers.MODIFY_OPERATION)
예제 #24
0
    def post(self):
        """Handle a post request."""
        name = self.request.get('name')
        if not name:
            raise helpers.EarlyExitException('Please give this corpus a name!',
                                             400)

        if not data_types.DataBundle.VALID_NAME_REGEX.match(name):
            raise helpers.EarlyExitException(
                'Name can only contain letters, numbers, dashes and underscores.',
                400)

        user_email = helpers.get_user_email()
        bucket_name = data_handler.get_data_bundle_bucket_name(name)
        bucket_url = data_handler.get_data_bundle_bucket_url(name)
        is_local = not self.request.get('nfs', False)

        if not data_handler.create_data_bundle_bucket_and_iams(
                name, [user_email]):
            raise helpers.EarlyExitException(
                'Failed to create bucket %s.' % bucket_name, 400)

        data_bundle = data_types.DataBundle.query(
            data_types.DataBundle.name == name).get()

        if not data_bundle:
            data_bundle = data_types.DataBundle()
        data_bundle.name = name
        data_bundle.bucket_name = bucket_name
        data_bundle.is_local = is_local
        data_bundle.put()

        template_values = {
            'title':
            'Success',
            'message':
            ('Upload data to the corpus using: '
             'gsutil -d -m rsync -r <local_corpus_directory> %s' % bucket_url),
        }
        self.render('message.html', template_values)
예제 #25
0
  def create_issue(testcase, severity, cc_me):
    """Create an issue."""
    issue_tracker = helpers.get_issue_tracker_for_testcase(testcase)
    user_email = helpers.get_user_email()

    if severity is not None:
      severity = helpers.cast(
          severity, int, 'Invalid value for security severity (%s).' % severity)

    additional_ccs = []
    if cc_me == 'true':
      additional_ccs.append(user_email)

    issue_id = issue_filer.file_issue(
        testcase,
        issue_tracker,
        security_severity=severity,
        user_email=user_email,
        additional_ccs=additional_ccs)

    if not issue_id:
      raise helpers.EarlyExitException('Unable to create new issue.', 500)
예제 #26
0
def get_results():
    """Return results."""
    is_user = access.has_access()
    user_email = helpers.get_user_email()
    external_jobs = external_users.allowed_jobs_for_user(user_email)

    is_external_user = not is_user and external_jobs
    if not is_user and not is_external_user:
        raise helpers.AccessDeniedException()

    if is_user:
        projects = _get_all_project_results()
    else:
        projects = _get_project_results_for_external_user(external_jobs)

    results = {
        'info': {
            'projects': projects,
            'is_internal_user': is_user,
        },
    }
    return results
예제 #27
0
  def handle_exception(self, exception, _):
    """Catch exception and format it properly."""
    try:

      status = 500
      values = {
          'message': exception.message,
          'email': helpers.get_user_email(),
          'traceDump': traceback.format_exc(),
          'status': status,
          'type': exception.__class__.__name__
      }
      if isinstance(exception, helpers.EarlyExitException):
        status = exception.status
        values = exception.to_dict()
      values['params'] = self.request.params.dict_of_lists()

      # 4XX is not our fault. Therefore, we hide the trace dump and log on
      # the INFO level.
      if status >= 400 and status <= 499:
        logging.info(json.dumps(values, cls=JsonEncoder))
        del values['traceDump']
      else:  # Other error codes should be logged with the EXCEPTION level.
        logging.exception(exception)

      if helpers.should_render_json(
          self.request.headers.get('accept', ''),
          self.response.headers.get('Content-Type')):
        self.render_json(values, status)
      else:
        if status == 403 or status == 401:
          self.render_forbidden(exception.message)
        else:
          self.render('error.html', values, status)
    except Exception:
      self.handle_exception_exception()
예제 #28
0
    def do_post(self):
        """Upload a testcase."""
        testcase_id = self.request.get('testcaseId')
        uploaded_file = self.get_upload()
        if testcase_id and not uploaded_file:
            testcase = helpers.get_testcase(testcase_id)
            if not access.can_user_access_testcase(testcase):
                raise helpers.AccessDeniedException()

            # Use minimized testcase for upload (if available).
            key = (testcase.minimized_keys if testcase.minimized_keys
                   and testcase.minimized_keys != 'NA' else
                   testcase.fuzzed_keys)

            uploaded_file = blobs.get_blob_info(key)

            # Extract filename part from blob.
            uploaded_file.filename = os.path.basename(
                uploaded_file.filename.replace('\\', os.sep))

        job_type = self.request.get('job')
        if not job_type:
            raise helpers.EarlyExitException('Missing job name.', 400)

        if (not data_types.Job.VALID_NAME_REGEX.match(job_type)
                or not data_types.Job.query(
                    data_types.Job.name == job_type).get()):
            raise helpers.EarlyExitException('Invalid job name.', 400)

        fuzzer_name = ''
        job_type_lowercase = job_type.lower()
        if 'libfuzzer' in job_type_lowercase:
            fuzzer_name = 'libFuzzer'
        elif 'afl' in job_type_lowercase:
            fuzzer_name = 'afl'

        target_name = self.request.get('target')
        if not fuzzer_name and target_name:
            raise helpers.EarlyExitException(
                'Target name is not applicable to non-engine jobs (AFL, libFuzzer).',
                400)

        if fuzzer_name and not target_name:
            raise helpers.EarlyExitException(
                'Missing target name for engine job (AFL, libFuzzer).', 400)

        if (target_name
                and not data_types.Fuzzer.VALID_NAME_REGEX.match(target_name)):
            raise helpers.EarlyExitException('Invalid target name.', 400)

        fully_qualified_fuzzer_name = ''
        if fuzzer_name and target_name:
            fully_qualified_fuzzer_name, target_name = find_fuzz_target(
                fuzzer_name, target_name, job_type)
            if not fully_qualified_fuzzer_name:
                raise helpers.EarlyExitException('Target does not exist.', 400)

        if not access.has_access(need_privileged_access=False,
                                 job_type=job_type,
                                 fuzzer_name=(fully_qualified_fuzzer_name
                                              or fuzzer_name)):
            raise helpers.AccessDeniedException()

        multiple_testcases = bool(self.request.get('multiple'))
        http_flag = bool(self.request.get('http'))
        high_end_job = bool(self.request.get('highEnd'))
        bug_information = self.request.get('issue')
        crash_revision = self.request.get('revision')
        timeout = self.request.get('timeout')
        retries = self.request.get('retries')
        bug_summary_update_flag = bool(self.request.get('updateIssue'))
        additional_arguments = self.request.get('args')
        app_launch_command = self.request.get('cmd')
        platform_id = self.request.get('platform')

        testcase_metadata = self.request.get('metadata')
        if testcase_metadata:
            try:
                testcase_metadata = json.loads(testcase_metadata)
                if not isinstance(testcase_metadata, dict):
                    raise helpers.EarlyExitException(
                        'Metadata is not a JSON object.', 400)
            except Exception:
                raise helpers.EarlyExitException('Invalid metadata JSON.', 400)

        archive_state = 0
        bundled = False
        file_path_input = ''
        email = helpers.get_user_email()

        # If we have a AFL or libFuzzer target, use that for arguments.
        # Launch command looks like
        # python launcher.py {testcase_path} {target_name}
        if target_name:
            additional_arguments = '%%TESTCASE%% %s' % target_name

        # Certain modifications such as app launch command, issue updates are only
        # allowed for privileged users.
        privileged_user = access.has_access(need_privileged_access=True)
        if not privileged_user:
            if bug_information or bug_summary_update_flag:
                raise helpers.EarlyExitException(
                    'You are not privileged to update existing issues.', 400)

            need_privileged_access = utils.string_is_true(
                data_handler.get_value_from_job_definition(
                    job_type, 'PRIVILEGED_ACCESS'))
            if need_privileged_access:
                raise helpers.EarlyExitException(
                    'You are not privileged to run this job type.', 400)

            if app_launch_command:
                raise helpers.EarlyExitException(
                    'You are not privileged to run arbitary launch commands.',
                    400)

            if testcase_metadata:
                raise helpers.EarlyExitException(
                    'You are not privileged to set testcase metadata.', 400)

        if crash_revision and crash_revision.isdigit():
            crash_revision = int(crash_revision)
        else:
            crash_revision = 0

        if bug_information and not bug_information.isdigit():
            raise helpers.EarlyExitException('Bug is not a number.', 400)

        if not timeout:
            timeout = 0
        elif not timeout.isdigit() or timeout == '0':
            raise helpers.EarlyExitException(
                'Testcase timeout must be a number greater than 0.', 400)
        else:
            timeout = int(timeout)
            if timeout > 120:
                raise helpers.EarlyExitException(
                    'Testcase timeout may not be greater than 120 seconds.',
                    400)

        if retries:
            if retries.isdigit():
                retries = int(retries)
            else:
                retries = None

            if retries is None or retries > MAX_RETRIES:
                raise helpers.EarlyExitException(
                    'Testcase retries must be a number less than %d.' %
                    MAX_RETRIES, 400)
        else:
            retries = None

        try:
            gestures = ast.literal_eval(self.request.get('gestures'))
        except:
            gestures = []
        if not gestures:
            gestures = []

        job_queue = tasks.queue_for_job(job_type, is_high_end=high_end_job)

        if uploaded_file is not None:
            filename = ''.join([
                x for x in uploaded_file.filename
                if x not in ' ;/?:@&=+$,{}|<>()\\'
            ])
            key = str(uploaded_file.key())
            if archive.is_archive(filename):
                archive_state = data_types.ArchiveStatus.FUZZED
            if archive_state:
                if multiple_testcases:
                    if testcase_metadata:
                        raise helpers.EarlyExitException(
                            'Testcase metadata not supported with multiple testcases.',
                            400)
                    # Create a job to unpack an archive.
                    metadata = data_types.BundledArchiveMetadata()
                    metadata.blobstore_key = key
                    metadata.timeout = timeout
                    metadata.job_queue = job_queue
                    metadata.job_type = job_type
                    metadata.http_flag = http_flag
                    metadata.archive_filename = filename
                    metadata.uploader_email = email
                    metadata.gestures = gestures
                    metadata.crash_revision = crash_revision
                    metadata.additional_arguments = additional_arguments
                    metadata.bug_information = bug_information
                    metadata.platform_id = platform_id
                    metadata.app_launch_command = app_launch_command
                    metadata.fuzzer_name = fuzzer_name
                    metadata.overridden_fuzzer_name = fully_qualified_fuzzer_name
                    metadata.fuzzer_binary_name = target_name
                    metadata.put()

                    tasks.add_task('unpack',
                                   str(metadata.key.id()),
                                   job_type,
                                   queue=tasks.queue_for_job(job_type))

                    # Create a testcase metadata object to show the user their upload.
                    upload_metadata = data_types.TestcaseUploadMetadata()
                    upload_metadata.timestamp = datetime.datetime.utcnow()
                    upload_metadata.filename = filename
                    upload_metadata.blobstore_key = key
                    upload_metadata.original_blobstore_key = key
                    upload_metadata.status = 'Pending'
                    upload_metadata.bundled = True
                    upload_metadata.uploader_email = email
                    upload_metadata.retries = retries
                    upload_metadata.bug_summary_update_flag = bug_summary_update_flag
                    upload_metadata.put()

                    helpers.log('Uploaded multiple testcases.',
                                helpers.VIEW_OPERATION)
                    self.render_json({'multiple': True})
                    return

                file_path_input = guess_input_file(uploaded_file, filename)
                if not file_path_input:
                    raise helpers.EarlyExitException((
                        "Unable to detect which file to launch. The main file\'s name "
                        'must contain either of %s.' % str(RUN_FILE_PATTERNS)),
                                                     400)

        else:
            raise helpers.EarlyExitException('Please select a file to upload.',
                                             400)

        testcase_id = data_handler.create_user_uploaded_testcase(
            key,
            key,
            archive_state,
            filename,
            file_path_input,
            timeout,
            job_type,
            job_queue,
            http_flag,
            gestures,
            additional_arguments,
            bug_information,
            crash_revision,
            email,
            platform_id,
            app_launch_command,
            fuzzer_name,
            fully_qualified_fuzzer_name,
            target_name,
            bundled,
            retries,
            bug_summary_update_flag,
            additional_metadata=testcase_metadata)

        testcase = data_handler.get_testcase_by_id(testcase_id)
        issue = issue_tracker_utils.get_issue_for_testcase(testcase)
        if issue:
            report_url = data_handler.TESTCASE_REPORT_URL.format(
                domain=data_handler.get_domain(), testcase_id=testcase_id)
            comment = ('ClusterFuzz is analyzing your testcase. '
                       'Developers can follow the progress at %s.' %
                       report_url)
            issue.save(new_comment=comment)

        helpers.log('Uploaded testcase %s' % testcase_id,
                    helpers.VIEW_OPERATION)
        self.render_json({'id': '%s' % testcase_id})
예제 #29
0
 def test_get_user_email_failure(self):
     """Ensure it gets empty string when a user is invalid."""
     self.mock.get_current_user.side_effect = Exception()
     self.assertEqual(helpers.get_user_email(), '')
예제 #30
0
 def test_get_user_email_success(self):
     """Ensure it gets the email when a user is valid."""
     self.mock.get_current_user.return_value = (auth.User('*****@*****.**'))
     self.assertEqual(helpers.get_user_email(), '*****@*****.**')