##// END OF EJS Templates
rcextensions: added example how to validate file size and name using rcextensions
marcink -
r3475:3b4cd059 default
parent child Browse files
Show More
@@ -0,0 +1,110 b''
1 # Example to validate pushed files names and size using some sort of rules
2
3
4
5 @has_kwargs({
6 'server_url': 'url of instance that triggered this hook',
7 'config': 'path to .ini config used',
8 'scm': 'type of version control "git", "hg", "svn"',
9 'username': 'username of actor who triggered this event',
10 'ip': 'ip address of actor who triggered this hook',
11 'action': '',
12 'repository': 'repository name',
13 'repo_store_path': 'full path to where repositories are stored',
14 'commit_ids': 'pre transaction metadata for commit ids',
15 'hook_type': '',
16 'user_agent': 'Client user agent, e.g git or mercurial CLI version',
17 })
18 def _pre_push_hook(*args, **kwargs):
19 """
20 Post push hook
21 To stop version control from storing the transaction and send a message to user
22 use non-zero HookResponse with a message, e.g return HookResponse(1, 'Not allowed')
23
24 This message will be shown back to client during PUSH operation
25
26 Commit ids might look like that::
27
28 [{u'hg_env|git_env': ...,
29 u'multiple_heads': [],
30 u'name': u'default',
31 u'new_rev': u'd0befe0692e722e01d5677f27a104631cf798b69',
32 u'old_rev': u'd0befe0692e722e01d5677f27a104631cf798b69',
33 u'ref': u'',
34 u'total_commits': 2,
35 u'type': u'branch'}]
36 """
37 import fnmatch
38 from .helpers import extra_fields, extract_pre_files
39 from .utils import str2bool, aslist
40 from rhodecode.lib.helpers import format_byte_size_binary
41
42 # returns list of dicts with key-val fetched from extra fields
43 repo_extra_fields = extra_fields.run(**kwargs)
44
45 # optionally use 'extra fields' to control the logic per repo
46 # e.g store a list of patterns to be forbidden e.g `*.exe, *.dump`
47 forbid_files = repo_extra_fields.get('forbid_files_glob', {}).get('field_value')
48 forbid_files = aslist(forbid_files)
49
50 # optionally get bytes limit for a single file, e.g 1024 for 1KB
51 forbid_size_over = repo_extra_fields.get('forbid_size_over', {}).get('field_value')
52 forbid_size_over = int(forbid_size_over or 0)
53
54 def validate_file_name_and_size(file_data, forbidden_files=None, size_limit=None):
55 """
56 This function validates commited files against some sort of rules.
57 It should return a valid boolean, and a reason for failure
58
59 file_data =[
60 'raw_diff', 'old_revision', 'stats', 'original_filename', 'is_limited_diff',
61 'chunks', 'new_revision', 'operation', 'exceeds_limit', 'filename'
62 ]
63 file_data['ops'] = {
64 # is file binary
65 'binary': False,
66
67 # lines
68 'added': 32,
69 'deleted': 0
70
71 'ops': {3: 'modified file'},
72 'new_mode': '100644',
73 'old_mode': None
74 }
75 """
76 file_name = file_data['filename']
77 operation = file_data['operation'] # can be A(dded), M(odified), D(eleted)
78
79 # check files names
80 if forbidden_files:
81 reason = 'File {} is forbidden to be pushed'.format(file_name)
82 for forbidden_pattern in forbid_files:
83 # here we can also filter for operation, e.g if check for only ADDED files
84 # if operation == 'A':
85 if fnmatch.fnmatch(file_name, forbidden_pattern):
86 return False, reason
87
88 # validate A(dded) files and size
89 if size_limit and operation == 'A':
90 size = len(file_data['raw_diff'])
91
92 reason = 'File {} size of {} bytes exceeds limit {}'.format(
93 file_name, format_byte_size_binary(size),
94 format_byte_size_binary(size_limit))
95 if size > size_limit:
96 return False, reason
97
98 return True, ''
99
100 if forbid_files or forbid_size_over:
101 # returns list of dicts with key-val fetched from extra fields
102 file_list = extract_pre_files.run(**kwargs)
103
104 for file_data in file_list:
105 file_valid, reason = validate_file_name_and_size(
106 file_data, forbid_files, forbid_size_over)
107 if not file_valid:
108 return HookResponse(1, reason)
109
110 return HookResponse(0, '')
@@ -0,0 +1,96 b''
1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2016-2019 RhodeCode GmbH
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
6 # (only), as published by the Free Software Foundation.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU Affero General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 #
16 # This program is dual-licensed. If you wish to learn more about the
17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19
20 """
21 us in hooks::
22
23 from .helpers import extract_pre_files
24 # returns list of dicts with key-val fetched from extra fields
25 file_list = extract_pre_files.run(**kwargs)
26
27 """
28 import re
29 import collections
30 import json
31
32 from rhodecode.lib import diffs
33 from rhodecode.lib.vcs.backends.hg.diff import MercurialDiff
34 from rhodecode.lib.vcs.backends.git.diff import GitDiff
35
36
37 def get_hg_files(repo, refs):
38 files = []
39 return files
40
41
42 def get_git_files(repo, refs):
43 files = []
44
45 for data in refs:
46 # we should now extract commit data
47 old_rev = data['old_rev']
48 new_rev = data['new_rev']
49
50 if '00000000' in old_rev:
51 # new branch, we don't need to extract nothing
52 return files
53
54 git_env = dict(data['git_env'])
55
56 cmd = [
57 'diff', old_rev, new_rev
58 ]
59
60 stdout, stderr = repo.run_git_command(cmd, extra_env=git_env)
61 vcs_diff = GitDiff(stdout)
62
63 diff_processor = diffs.DiffProcessor(vcs_diff, format='newdiff')
64 # this is list of dicts with diff information
65 # _parsed[0].keys()
66 # ['raw_diff', 'old_revision', 'stats', 'original_filename',
67 # 'is_limited_diff', 'chunks', 'new_revision', 'operation',
68 # 'exceeds_limit', 'filename']
69 files = _parsed = diff_processor.prepare()
70
71 return files
72
73
74 def run(*args, **kwargs):
75 from rhodecode.model.db import Repository
76
77 vcs_type = kwargs['scm']
78 # use temp name then the main one propagated
79 repo_name = kwargs.pop('REPOSITORY', None) or kwargs['repository']
80
81 repo = Repository.get_by_repo_name(repo_name)
82 vcs_repo = repo.scm_instance(cache=False)
83
84 files = []
85
86 if vcs_type == 'git':
87 for rev_data in kwargs['commit_ids']:
88 new_environ = dict((k, v) for k, v in rev_data['git_env'])
89 files = get_git_files(vcs_repo, kwargs['commit_ids'])
90
91 if vcs_type == 'hg':
92 for rev_data in kwargs['commit_ids']:
93 new_environ = dict((k, v) for k, v in rev_data['hg_env'])
94 files = get_hg_files(vcs_repo, kwargs['commit_ids'])
95
96 return files
@@ -1,92 +1,79 b''
1 1 # Example to validate commit message or author using some sort of rules
2 2
3 3
4 4 @has_kwargs({
5 5 'server_url': 'url of instance that triggered this hook',
6 6 'config': 'path to .ini config used',
7 7 'scm': 'type of version control "git", "hg", "svn"',
8 8 'username': 'username of actor who triggered this event',
9 9 'ip': 'ip address of actor who triggered this hook',
10 10 'action': '',
11 11 'repository': 'repository name',
12 12 'repo_store_path': 'full path to where repositories are stored',
13 13 'commit_ids': 'pre transaction metadata for commit ids',
14 14 'hook_type': '',
15 15 'user_agent': 'Client user agent, e.g git or mercurial CLI version',
16 16 })
17 @has_kwargs({
18 'server_url': 'url of instance that triggered this hook',
19 'config': 'path to .ini config used',
20 'scm': 'type of version control "git", "hg", "svn"',
21 'username': 'username of actor who triggered this event',
22 'ip': 'ip address of actor who triggered this hook',
23 'action': '',
24 'repository': 'repository name',
25 'repo_store_path': 'full path to where repositories are stored',
26 'commit_ids': 'pre transaction metadata for commit ids',
27 'hook_type': '',
28 'user_agent': 'Client user agent, e.g git or mercurial CLI version',
29 })
30 17 def _pre_push_hook(*args, **kwargs):
31 18 """
32 19 Post push hook
33 20 To stop version control from storing the transaction and send a message to user
34 21 use non-zero HookResponse with a message, e.g return HookResponse(1, 'Not allowed')
35 22
36 23 This message will be shown back to client during PUSH operation
37 24
38 25 Commit ids might look like that::
39 26
40 27 [{u'hg_env|git_env': ...,
41 28 u'multiple_heads': [],
42 29 u'name': u'default',
43 30 u'new_rev': u'd0befe0692e722e01d5677f27a104631cf798b69',
44 31 u'old_rev': u'd0befe0692e722e01d5677f27a104631cf798b69',
45 32 u'ref': u'',
46 33 u'total_commits': 2,
47 34 u'type': u'branch'}]
48 35 """
49 36 import re
50 37 from .helpers import extra_fields, extract_pre_commits
51 38 from .utils import str2bool
52 39
53 40 # returns list of dicts with key-val fetched from extra fields
54 41 repo_extra_fields = extra_fields.run(**kwargs)
55 42
56 43 # optionally use 'extra fields' to control the logic per repo
57 44 validate_author = repo_extra_fields.get('validate_author', {}).get('field_value')
58 45 should_validate = str2bool(validate_author)
59 46
60 47 # optionally store validation regex into extra fields
61 48 validation_regex = repo_extra_fields.get('validation_regex', {}).get('field_value')
62 49
63 50 def validate_commit_message(commit_message, message_regex=None):
64 51 """
65 52 This function validates commit_message against some sort of rules.
66 53 It should return a valid boolean, and a reason for failure
67 54 """
68 55
69 56 if "secret_string" in commit_message:
70 57 msg = "!!Push forbidden: secret string found in commit messages"
71 58 return False, msg
72 59
73 60 if validation_regex:
74 61 regexp = re.compile(validation_regex)
75 62 if not regexp.match(message):
76 63 msg = "!!Push forbidden: commit message does not match regexp"
77 64 return False, msg
78 65
79 66 return True, ''
80 67
81 68 if should_validate:
82 69 # returns list of dicts with key-val fetched from extra fields
83 70 commit_list = extract_pre_commits.run(**kwargs)
84 71
85 72 for commit_data in commit_list:
86 73 message = commit_data['message']
87 74
88 75 message_valid, reason = validate_commit_message(message, validation_regex)
89 76 if not message_valid:
90 77 return HookResponse(1, reason)
91 78
92 79 return HookResponse(0, '')
General Comments 0
You need to be logged in to leave comments. Login now