Skip to content

Commit

Permalink
Merge pull request #1169 from dradis/methodologies-api
Browse files Browse the repository at this point in the history
Methodologies api
  • Loading branch information
caitmich authored Aug 28, 2023
2 parents 7064d95 + 10892da commit 7e532cd
Show file tree
Hide file tree
Showing 70 changed files with 2,866 additions and 94 deletions.
3 changes: 1 addition & 2 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
- [report type]:
- [future tense verb] [reporting enhancement]
- REST/JSON API enhancements:
- [API entity]:
- [future tense verb] [API enhancement]
- Boards, Lists, Cards: add initial implementation
- Security Fixes:
- High: (Authenticated|Unauthenticated) (admin|author|contributor) [vulnerability description]
- Medium: (Authenticated|Unauthenticated) (admin|author|contributor) [vulnerability description]
Expand Down
3 changes: 2 additions & 1 deletion app/models/card.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,15 @@ def to_xml(xml_builder, includes: [], version: 3)
end
end

private
def local_fields
{
'List' => list.name.parameterize(preserve_case: true, separator: '_'),
'Title' => name
}
end

private

# We are saving the board_id to the card's version so that if the card's list
# is deleted, we still have an idea if the card's board still exists.
def add_board_id_to_version
Expand Down
49 changes: 48 additions & 1 deletion config/brakeman.ignore
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,29 @@
],
"note": "False positive: Attachment.pwd is set by the admin to specify the directory for the attachments"
},
{
"warning_type": "File Access",
"warning_code": 16,
"fingerprint": "180c4b532ac14a974f17a0884b71b2dfd1bfe01815b4c84ae070f082842a49fc",
"check_name": "FileAccess",
"message": "Model attribute used in file name",
"file": "engines/dradis-api/app/controllers/dradis/ce/api/v3/attachments_controller.rb",
"line": 56,
"link": "https://brakemanscanner.org/docs/warning_types/file_access/",
"code": "File.rename(Attachment.find(params[:filename], :conditions => ({ :node_id => current_project.nodes.find(params[:node_id]).id })).fullpath, Attachment.pwd.join(current_project.nodes.find(params[:node_id]).id.to_s, CGI.unescape(attachment_params[:filename])).to_s)",
"render_path": null,
"location": {
"type": "method",
"class": "Dradis::CE::API::V3::AttachmentsController",
"method": "update"
},
"user_input": "Attachment.find(params[:filename], :conditions => ({ :node_id => current_project.nodes.find(params[:node_id]).id })).fullpath",
"confidence": "Medium",
"cwe_id": [
22
],
"note": "False positive: The destination filename is prepended by the Attachments directory and validated as such to prevent being moved to the other directories"
},
{
"warning_type": "File Access",
"warning_code": 16,
Expand Down Expand Up @@ -209,6 +232,30 @@
],
"note": "False positive: params[:uploader] here is being validated in the controller"
},
{
"warning_type": "Denial of Service",
"warning_code": 76,
"fingerprint": "d4b56fe0de40fbaed1bfdd6cc44d57ac2dc2b4aef5293c2afc326aef6e0c88e6",
"check_name": "RegexDoS",
"message": "Model attribute used in regular expression",
"file": "engines/dradis-api/app/controllers/dradis/ce/api/v3/attachments_controller.rb",
"line": 55,
"link": "https://brakemanscanner.org/docs/warning_types/denial_of_service/",
"code": "/^#{Attachment.pwd}/",
"render_path": null,
"location": {
"type": "method",
"class": "Dradis::CE::API::V3::AttachmentsController",
"method": "update"
},
"user_input": "Attachment.pwd",
"confidence": "Medium",
"cwe_id": [
20,
185
],
"note": "False positive: Attachment.pwd is set by the admin to specify the directory for the attachments"
},
{
"warning_type": "Remote Code Execution",
"warning_code": 24,
Expand Down Expand Up @@ -267,6 +314,6 @@
"note": "False positive: The params is used to fetch the boards and cannot be manipulated by user input"
}
],
"updated": "2023-03-30 20:21:19 +0800",
"updated": "2023-08-18 10:40:16 -0400",
"brakeman_version": "5.4.0"
}
2 changes: 2 additions & 0 deletions engines/dradis-api/CHANGELOG
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
v3 (Aug 2023)
- Add Boards, Lists, Cards endpoints.
20 changes: 20 additions & 0 deletions engines/dradis-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,25 @@
This plugin provides an external HTTP API that you can use to query / publish data to your Dradis instance.


## Bumping the API version

Rewatch: http://railscasts.com/episodes/350-rest-api-versioning

- When we bump the API version, we copy everything from the previous version and
start making changes while leaving the originals untouched.
- This means the entire controllers/vX/ and views/vX folders.
- Initially it duplicates the code, but eventually the new version is going to
evolve over time, while the original version will remain a snapshot of the
functionality that's frozen in time.
- I think we can safely deprecate older API versions after 2 years. See the
comments in the v1 controllers/ files for guidance on what to include in the
deprecated files.
- You'll also need to duplicate the routes block, and update the :default route
constraint to point to the new version.
- You'll need to duplicate the request specs too. Update the previous specs w/
a `let(:api_version)` block and `include_context 'versioned API'`
- Update the engine's CHANGELOG w/ a list of the breaking changes.


## Links, licensing, etc.
See the main repo's [README.md](https://github.com/dradis/dradis-ce/blob/master/README.md)
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ def set_node
def evidence_params
params.require(:evidence).permit(:content, :issue_id)
end

end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ def set_node
def note_params
params.require(:note).permit(:category_id, :text)
end

end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
module Dradis::CE::API
module V3
class AttachmentsController < Dradis::CE::API::APIController
include ActivityTracking
include Dradis::CE::API::ProjectScoped

before_action :set_node

skip_before_action :json_required, only: [:create]

def index
@attachments = @node.attachments.each(&:close)
end

def show
begin
@attachment = Attachment.find(params[:filename], conditions: { node_id: @node.id })
rescue
raise ActiveRecord::RecordNotFound, "Couldn't find attachment with filename '#{params[:filename]}'"
end
end

def create
uploaded_files = params.fetch(:files, [])

@attachments = []
uploaded_files.each do |uploaded_file|
attachment_name = NamingService.name_file(
original_filename: uploaded_file.original_filename,
pathname: Attachment.pwd.join(@node.id.to_s)
)

attachment = Attachment.new(attachment_name, node_id: @node.id)
attachment << uploaded_file.read
attachment.save

@attachments << attachment
end

if @attachments.any? && @attachments.count == uploaded_files.count
render status: 201
else
render status: 422
end
end

def update
attachment = Attachment.find(params[:filename], conditions: { node_id: @node.id })
attachment.close

begin
new_name = CGI::unescape(attachment_params[:filename])
destination = Attachment.pwd.join(@node.id.to_s, new_name).to_s

if !File.exist?(destination) && !destination.match(/^#{Attachment.pwd}/).nil?
File.rename attachment.fullpath, destination
@attachment = Attachment.find(new_name, conditions: { node_id: @node.id })
else
raise 'Destination file already exists'
end
rescue
@attachment = attachment
render status: 422
end
end

def destroy
@attachment = Attachment.find(params[:filename], conditions: { node_id: @node.id })
@attachment.delete

render_successful_destroy_message
end

private

def set_node
@node = current_project.nodes.find(params[:node_id])
end

def attachment_params
params.require(:attachment).permit(:filename)
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
module Dradis::CE::API
module V3
class BoardsController < Dradis::CE::API::APIController
include ActivityTracking
include Dradis::CE::API::ProjectScoped

def index
@boards = current_project.boards.includes(:lists, lists: [:cards]).order('updated_at desc')
@boards = @boards.page(params[:page].to_i) if params[:page]
end

def show
@board = current_project.boards.includes(:lists, lists: [:cards]).find(params[:id])
end

def create
@board = current_project.boards.new(board_params)
# we are mimicking the hidden_field used in the UI to set the node_id in CE
@board.node_id = current_project.methodology_library.id if !params[:node_id]

if @board.save
track_created(@board)
render status: 201, location: dradis_api.board_url(@board)
else
render_validation_errors(@board)
end
end

def update
@board = current_project.boards.find(params[:id])
if @board.update(board_params)
track_updated(@board)
render board: @board
else
render_validation_errors(@board)
end
end

def destroy
board = current_project.boards.find(params[:id])
board.destroy
track_destroyed(board)
render_successful_destroy_message
end

protected

def board_params
params.require(:board).permit(:name, :node_id)
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
module Dradis::CE::API
module V3
class CardsController < Dradis::CE::API::APIController
include ActivityTracking
include Dradis::CE::API::ProjectScoped

before_action :set_board
before_action :set_list

def index
@cards = @list.cards.includes(:assignees).order('updated_at desc')
@cards = @cards.page(params[:page].to_i) if params[:page]
end

def show
@card = @list.cards.includes(:assignees).find(params[:id])
end

def create
@card = @list.cards.build(card_params)
# Set the new card as the last card of the list
@card.previous_id = @list.last_card.try(:id)

if @card.save
track_created(@card)
render status: 201, location: board_list_card_path(@board, @list, @card)
else
render_validation_errors(@card)
end
end

def update
@card = @list.cards.find(params[:id])
if @card.update(card_params)
track_updated(@card)
render list: @card
else
render_validation_errors(@card)
end
end

def destroy
@card = @list.cards.find(params[:id])
@card.destroy
track_destroyed(@card)
render_successful_destroy_message
end

private

def set_board
@board = current_project.boards.includes(:lists).find(params[:board_id])
end

def set_list
@list = @board.lists.includes(:cards, cards: :assignees).find(params[:list_id])
end

def card_params
params.require(:card).permit(:name, :description, :due_date, assignee_ids: [])
end
end
end
end
Loading

0 comments on commit 7e532cd

Please sign in to comment.